O(nlogn)アルゴリズム-バイナリ文字列内で等間隔に配置された3つのものを見つける


173

昨日、アルゴリズムのテストでこの質問がありましたが、答えがわかりません。約40ポイントの価値があったので、それは私を完全に夢中にさせています。過去24時間以内に解決策を考え出していないため、クラスのほとんどが正しく解決しなかったと思います。

長さnの任意のバイナリ文字列が与えられた場合、文字列内に等間隔に配置された3つの文字列が存在する場合、それらを見つけます。これをO(n * log(n))時間で解決するアルゴリズムを記述します。

したがって、次のような文字列には「等間隔」の3つの文字列があります:11100000、0100100100

編集:これは乱数なので、任意の数で機能するはずです。私が挙げた例は、「等間隔」プロパティを説明するためのものです。したがって、1001011は有効な数値です。1、4、7は等間隔です。


4
以下は可能ですか:10011010000?3つの1(1番目、2番目、4番目)が等間隔に配置されていますが、追加の1もあります。
アンナ

5
ロバート、あなたは教授にこれに対する答えを与えて、ここに投稿する必要があります。この問題は私を壁に押し上げています。n ^ 2でそれを行う方法を理解できますが、n * log(n)ではできません。
James McMahon、

3
うーん私もこれを理解しようと長い間費やしましたが、まだ良い答えに出会っていません。おそらくあなたは質問を誤解しましたか?たとえば、質問が尋ねられた場合、O(n log n)で実行されるアルゴリズムを見つけます。これは、間隔kの等間隔のシーケンスの位置を決定します。これは、はるかに大きなシーケンスで、高速フーリエ変換を使用して簡単に実行できます。
ldog 2009年

2
あなたの教授が解決策を提供する場合は、回答として投稿してください。
ldog 2009年

5
(とりわけ)クラウスロスがフィールズメダル1958を取得したという事実を考慮して、d> 0の各密度に対して、少なくともd *の{1、...、N}の各サブセットが存在するような自然数Nがあることを証明します。 N個の要素には長さ3の算術の進行が含まれています。今まで誰も問題の説得力のあるアルゴリズムを見つけられなかったことに驚くことはありません。en.wikipedia.org/wiki/Szemer%C3%A9di%27s_theorem
jp

回答:


128

最後に!sdcvvcの答えでリードをフォローアップすると、問題があります。問題のO(n log n)アルゴリズムです!理解すれば簡単です。FFTを推測した人たちは正しかった。

問題:S長さnのバイナリ文字列が与えられ、その中に等間隔に配置された3つの1を見つけたい。例えば、Sとすることができる110110010、ここで、N = 9。2、5、および8の位置に1が等間隔に配置されています。

  1. S左から右にスキャンしL、位置のリストを1にします。S=110110010上記の場合、リストはL = [1、2、4、5、8]になります。このステップはO(n)です。問題を見つけるために今ある長さ3の等差数列でのL異なる見つけるために、すなわちA、B、CのLようBA = CB、または等価+ C = 2B。上記の例では、進行状況(2、5、8)を検索します。

  2. の各kに対して項x kをもつ多項式 pを作成します。上記の例では、多項式p(x)=(x + x 2 + x 4 + x 5 + x 8)を作成します。このステップはO(n)です。L

  3. 高速フーリエ変換を使用して、多項式q= p 2求めます。上記の例では、多項式q(x)= x 16 + 2x 13 + 2x 12 + 3x 10 + 4x 9 + x 8 + 2x 7 + 4x 6 + 2x 5 + x 4 + 2x 3 + x 2が得られます。このステップはO(n log n)です。

  4. のいくつかのkについて、x 2kに対応するものを除くすべての項を無視します。上記の例では、我々は、用語得るX 16、3× 10 X、8、xは4、xは2。このステップは、O(n)です。L

ここで重要な点は次のとおり任意の係数のX 2bのためのBには、Lある正確ペアの数(C)におけるLよう+ C = 2B。[CLRS、例 30.1-7]そのようなペアの1つは常に(b、b)です(したがって、係数は少なくとも1です)が、他のペア(a、c)が存在する場合、係数は(a、c)から少なくとも3 です。 )および(c、a)。上記の例では、AP(2,5,8)のため、x 10の係数は正確に3になります。(これらの係数x 2b上記の理由により、常に奇数になります。そして、qの他のすべての係数は常に偶数になります。)

したがって、アルゴリズムはこれらの項x 2bの係数を調べ、それらのいずれかが1より大きいかどうかを確認します。存在しない場合、等間隔の1はありません。存在した場合であり、BがLの係数れるX 2bが 1より大きい場合、我々はいくつかのペアが存在することを知っている(C)以外の- (B、B) -のために、A + C = 2bが。実際のペアを見つけるには、単純に各aを試しL(対応するc2b-aになります)、位置2b-aに1があるかどうかを確認しSます。このステップはO(n)です。

それだけです、皆さん。


FFTを使用する必要がありますか?betaflybywire、およびrspのような多くの答えは、1の各ペアをチェックし、「3番目」の位置に1があるかどうかを確認するアプローチは、直感に基づいてO(n log n)で機能する可能性があることを示唆しています。 1が多すぎるとトリプルが簡単に見つかります。1が少なすぎると、すべてのペアをチェックするのに時間がかかりません。残念ながら、この直感は正しく、シンプルなアプローチ O(n 2)よりも優れていますが、それほど優れているわけではありません。以下のようにsdcvvcの答えは、我々は長さの文字列の「カントールのようなセット」を取ることができ、N = 3 のk、3進数表現に0と2のみ(1はなし)が含まれる位置に1があります。このような文字列には2 k = n (log 2)/(log 3) ≈n 0.63のものが1つあり、1は等間隔ではないため、すべてのペアをチェックすると、その中の1の数の2乗のオーダーになります。4 K ≈N 1.26残念ながら漸近(NログN)よりもはるかに大きいです。実際、最悪のケースはさらに悪化します。1953年のLeo Moserは、n 1-c /√(log n) 1を含むが効果的に等間隔の1を持たない文字列を構築しました。つまり、そのような文字列では、単純なアプローチはΘ(n 2-2c /√(log n)をとります-唯一の小さなよりも良好なビットΘ(nは2、驚くほど!


等間隔に配置された3つがない長さnの文字列の1の最大数について(上で見たものは、簡単なCantorのような構造から少なくともn 0.63であり、少なくともn 1-c /√(log n)でMoserの構造)—これはOEIS A003002です。また、OEIS A065825からkとして直接計算することもでき、A065825(k)≤n <A065825(k + 1)となります。私はこれらを見つけるためのプログラムを書きました、そして、貪欲なアルゴリズムはそのような最長の文字列を与えないことがわかりました。例えば、のためのn = 9、我々は5 1S(110100011)を得ることができるが、貪欲は、4(110110000)、のための得られるn個= 26は11 1s(11001010001000010110001101)を取得できますが、貪欲は8(11011000011011000000000000)のみを提供し、n = 74は22 1s(11000010110001000001011010001000000000000000010001011010000010001101000011)を取得できますが、貪欲は16(11011000011011000000000000011011000011011000000000000000000000110000000000000000000)を提供します。彼らは50までかなりの数の場所で同意します(たとえば、38〜50のすべて)。OEISの参考文献が言うように、Jaroslaw Wroblewskiはこの質問に興味を持っているようで、彼はこれらの非平均化セットに関するWebサイトを維持しています。正確な数は194までしか知られていません。


27
非常に素晴らしい。印象的です。テストで誰かがこれを考え出すことを期待することは少し多くのようです。
hughdbrown、2009年

4
まあ、ステップ1、問題をAPを見つけることに変換することは簡単です。ステップ3、多項式はO(n log n)時間で乗算できることは、単なる事実です。実際のトリック、および問題を困難にしているのは、11011を係数[1,1,0,1,1]などの多項式と考えるという考えです。これは賢い、多くの場合有用な考えです。オイラーに戻る方法。[現代の博覧会についてはWilfの素晴らしい本「generatingfunctionology」を参照してください:math.upenn.edu/~wilf/DownldGF.html)したがって、学生が最近の記憶で関数を生成することに触れたかどうかによって異なります。:-)
ShreevatsaR

2
すみません、計算が完全に間違っていました。110110010 ^ 2 = 12124214302200100である必要があります。しかし、アイデアは立っています。ただ、3の位置に注意してください
ギジェルモ・フィリップス

11
非常に印象的。このスレッド/質問が一緒になって解決策を見つけるのを見るのは本当にクールです。それは不可能だと思い始めていました。また、この教授は悪です。
KingNestor

1
@RexE:pが次数n-1(n項を持つ)の場合、q = p ^ 2は次数2n-2(最大2n-1項を持つ)になります。どのようにしてn ^ 2を手に入れましたか?(また、FFTを使用してO(n log n)時間で次数nの2つの多項式を乗算することはかなり標準的な操作です。回答のリンクをクリックするか、Wikipediaの記事を参照してください。)
ShreevatsaR

35

この問題は、この論文(1999)では平均と呼ばれています

問題3SUMから二次の縮小がある場合、問題は3SUM困難です。n個の整数のセットAが与えられた場合、Aにa + b + c = 0であるような要素a、b、cがありますか?AVERAGEが3SUM-hardであるかどうかは不明です。ただし、AVERAGEから3SUMへの単純な線形時間の短縮があり、その説明は省略します。

ウィキペディア

整数が[-u ... u]の範囲にある場合、Sをビットベクトルとして表し、FFTを使用してたたみ込みを実行することにより、3SUMを時間O(n + u lg u)で解くことができます。

これはあなたの問題を解決するのに十分です:)。

何が非常に重要なことは(Nログn)は0と1の数の面で複雑であるOである、もののない数(のように、配列として指定することができ[1,5,9,15])。セットが1の数の項である算術列を持っているかどうかのチェックは困難であり、1999年現在のその論文によると、O(n 2)よりも速いアルゴリズムは知られていないため、存在しないと推測されます。これを考慮しない人は誰でも、未解決の問題を解決しようとしています。

その他の興味深い情報、主に反抗的:

下限:

簡単な下限はCantorのようなセット(3進展開で1を含まない1..3 ^ n-1の数値)です-その密度はn ^(log_3 2)です(およそ0.631)。したがって、セットが大きすぎないかどうかを確認し、すべてのペアを確認するだけでは、O(n log n)を取得するには不十分です。シーケンスをよりスマートに調査する必要があります。より良い下限がここに引用されています -それはn 1-c /(log(n))^(1/2)です。つまり、Cantorセットは最適ではありません

上限-私の古いアルゴリズム:

大きなnの場合、算術進行を含まない{1,2、...、n}のサブセットは最大でn /(log n)^(1/20)要素を持つことが知られています。算術プログレッションのトリプルについての論文は、より多くを証明します:セットは、n * 2 28 *(log log n / log n)1/2要素を超えることはできません。したがって、その境界が達成されているかどうかを確認し、達成されていない場合は単純にペアを確認できます。これはO(n 2 * log log n / log n)アルゴリズムで、O(n 2)より高速です。残念ながら、 "On triples ..."はSpringerにありますが、最初のページが利用可能であり、Ben Greenの説明は、28ページ、定理24で利用可能です

ちなみに、論文は1999年のものです-私が最初に述べたのと同じ年なので、おそらくそれが最初の論文がその結果について言及していない理由です。


2
すばらしい答えです。最初の問題は、この問題について決定的なことを述べています。したがって、Cantorのようなセットにはn ^ 0.63 1があります。つまり、「1のすべてのペアをチェックする」アルゴリズムは、最悪の場合、少なくともn ^ 1.26(≫ n log n)です。Szemerediの論文で引用された下限(BTWが引用したモーザーの論文はこちらから入手できます:books.google.com/books ?id=Cvtwu5vVZF4C&pg=PA245 )は、実際にはn ^(2-o(1))を意味しているようですが、 {1、...、n}から得られた数値があるので少し注意してください。ただし、これはnであるシーケンスの数値の合計です。
ShreevatsaR

えっと、n ^(log_3 2)1を含み、3つの等間隔の1を含まない "Cantorのような"バイナリシーケンスとは何ですか?
ShreevatsaR

例:101000101000000000101000101。長さは3 ^ nで、2 ^ n個あります(つまりn ^ 0.63の密度)。1の場所をバイナリで書き留めると、{0,2,20,22,200,202,220,222}になります。それを考える別の可能な方法は、1のシーケンスを取り、通常のCantorセット構築のように「中間」のものを継続的に削除することです:111111111-> 111000111->101000101。算術の進行が含まれない理由は、 、y、zは1を形成し、y =(x + z)/ 2であり、xとzは展開場所によって異なります。最も重要なものを取る。たとえば、xには0があり、zには2があるとします。その場合、yには1が必要です。矛盾。
sdcvvc 2009年

3
繰り返しますが、すばらしい研究です!私は2008 3SUM論文をフォローアップし、CLRS演習について言及しました。30.1-7、私が答えを得たところを見た後-O(n log n)アルゴリズムは実際には非常に単純です!(多項式/生成関数を二乗するだけです。)以下の答えを投稿しました。(今、以前はそれについて考えていなかったので自分を蹴り飛ばしています...単純な解決策は常にその反応を引き出します:p)
ShreevatsaR

したがって、彼の試験問題への答えは次のようなものでした。「この問題は3-SUMのハード問題に還元可能であり、3-SUMのハードは二次の解を持たないため、この問題はO(n logn)で解決できません。 」はい?
hughdbrown、2009年

8

これは解決策ではありませんが、オレキシーが考えていたのと同様の考え方

最大数が1のシーケンスを作成するために遊んでいましたが、それらはすべて非常に興味深いものでした。125桁まで取得しました。可能な限り多くの「1」ビットを挿入しようとしたときに見つかった最初の3つの数値は次のとおりです。

  • 11011000011011000000000000001101100001101100000000000000000000000000000000000000000110110000110110000000000000011011000011011
  • 10110100010110100000000000010110100010110100000000000000000000000000000000000000000101101000101101000000000000101101000101101
  • 10011001010011001000000000010011001010011001000000000000000000000000000000000000010011001010011001000000000010011001010011001

それらはすべてフラクタルであることに注意してください(制約を考えるとそれほど驚くべきことではありません)。後ろ向きに考えることに何かがあるかもしれません、おそらく文字列が特性を持つフラクタルでない場合、それは繰り返しパターンを持つ必要がありますか?

これらの数値を説明するためのより良い用語についてベータに感謝します。

更新: 悲しいかな、10000000000001のような十分に大きな初期文字列で開始すると、パターンが壊れるように見えます。

100000000000011
10000000000001101
100000000000011011
10000000000001101100001
100000000000011011000011
10000000000001101100001101
100000000000011011000011010000000001
100000000000011011000011010000000001001
1000000000000110110000110100000000010011
1000000000000110110000110100000000010011001
10000000000001101100001101000000000100110010000000001
10000000000001101100001101000000000100110010000000001000001
1000000000000110110000110100000000010011001000000000100000100000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011
1000000000000110110000110100000000010011001000000000100000100000000000001101
100000000000011011000011010000000001001100100000000010000010000000000000110100001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011001000000000000000000000010010000010000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001001000000000000000000000000000000000000110010000000000000000000000100100000100000011
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001000001000000110000000000001

2
聖なる* @ !!、これらはフラクタルです!これが成り立つ場合は、1の数に上限を設定し、O(n)未満にします。
ベータ

フラクタル、それはそれらを説明するためのはるかに良い言葉です。ありがとう
z-

興味深いことに、これらのパターンはCantorの3値セット(en.wikipedia.org/wiki/Cantor_set)によく似ています。これがそうである場合、1の割合はゼロになる傾向があります...
flybywire

トリプルなしで最大数1のシーケンスがアルゴリズムの最悪の実行時間に直接関連していることは明らかですか?1がたくさんある文字列が考えられますが、これらの1はアルゴリズムによって遅く検査される位置にあるため、トリプルが非常に遅く検出されるだけです。
ShreevatsaR

3
全体のサイズと比較した文字列の1の数の分析は、1の数と文字列のサイズの間に線形関係があることを示しているように思われ、私は、文字列内の1の数は、指定された文字列に対して最大でlog(n)になります。したがって、文字列全体ではなく、1の位置のみを処理するソリューションもO(n ^ 2)になります。または、より正確には、O(n + m ^ 2)です。ここで、mは文字列内の1の数、nは文字列のサイズ、mはbig-theta(n)です。
ウェルボグ2009年

6

O(n ^ 2)のように見える単純なアプローチは、実際にはO(n ln(n))のようなより良いものをもたらすと思います。(任意のnについて)テストに最も時間がかかるシーケンスは、トリオを含まないシーケンスであり、シーケンスに含めることができる1の数に厳しい制限を課します。

私はいくつかの手を振る議論を思いつきましたが、きちんとした証拠を見つけることができませんでした。私は暗闇の中で試してみるつもりです。答えは、教授が長い間知っていた非常に巧妙なアイデアであり、明白に見えるようになりましたが、学生にとっては非常に難しいものです。(それか、それを取り上げた講義で眠っていました。)


2
笑、いや、講義は寝てなかった。私は他の数人の学生と話しました、そして、それを解決する方法について明確な考えを持った人はいませんでした。ほとんどの人は、ある程度の信用を得るために、分割と征服を嘆願していくつかのBSを書きました。
ロバートパーカー、

3

改訂:2009-10-17 23:00

私はこれを(2000万の文字列などの)大きな数で実行しましたが、このアルゴリズムはO(n logn)ではないと思います。それにもかかわらず、十分に優れた実装であり、非常に高速に実行するための多数の最適化が含まれています。25秒未満で24桁以下のバイナリ文字列のすべての配置を評価します。

0 <= L < M < U <= X-1今日の初めからの観察を含むようにコードを更新しました。


元の

これは、概念的には、私が回答した別の質問に似ています。このコードはまた、一連の3つの値を調べ、トリプレットが条件を満たすかどうかを判断しました。これを元にしたC#コードを次に示します。

using System;
using System.Collections.Generic;

namespace StackOverflow1560523
{
    class Program
    {
        public struct Pair<T>
        {
            public T Low, High;
        }
        static bool FindCandidate(int candidate, 
            List<int> arr, 
            List<int> pool, 
            Pair<int> pair, 
            ref int iterations)
        {
            int lower = pair.Low, upper = pair.High;
            while ((lower >= 0) && (upper < pool.Count))
            {
                int lowRange = candidate - arr[pool[lower]];
                int highRange = arr[pool[upper]] - candidate;
                iterations++;
                if (lowRange < highRange)
                    lower -= 1;
                else if (lowRange > highRange)
                    upper += 1;
                else
                    return true;
            }
            return false;
        }
        static List<int> BuildOnesArray(string s)
        {
            List<int> arr = new List<int>();
            for (int i = 0; i < s.Length; i++)
                if (s[i] == '1')
                    arr.Add(i);
            return arr;
        }
        static void BuildIndexes(List<int> arr, 
            ref List<int> even, ref List<int> odd, 
            ref List<Pair<int>> evenIndex, ref List<Pair<int>> oddIndex)
        {
            for (int i = 0; i < arr.Count; i++)
            {
                bool isEven = (arr[i] & 1) == 0;
                if (isEven)
                {
                    evenIndex.Add(new Pair<int> {Low=even.Count-1, High=even.Count+1});
                    oddIndex.Add(new Pair<int> {Low=odd.Count-1, High=odd.Count});
                    even.Add(i);
                }
                else
                {
                    oddIndex.Add(new Pair<int> {Low=odd.Count-1, High=odd.Count+1});
                    evenIndex.Add(new Pair<int> {Low=even.Count-1, High=even.Count});
                    odd.Add(i);
                }
            }
        }

        static int FindSpacedOnes(string s)
        {
            // List of indexes of 1s in the string
            List<int> arr = BuildOnesArray(s);
            //if (s.Length < 3)
            //    return 0;

            //  List of indexes to odd indexes in arr
            List<int> odd = new List<int>(), even = new List<int>();

            //  evenIndex has indexes into arr to bracket even numbers
            //  oddIndex has indexes into arr to bracket odd numbers
            List<Pair<int>> evenIndex = new List<Pair<int>>(), 
                oddIndex = new List<Pair<int>>(); 
            BuildIndexes(arr, 
                ref even, ref odd, 
                ref evenIndex, ref oddIndex);

            int iterations = 0;
            for (int i = 1; i < arr.Count-1; i++)
            {
                int target = arr[i];
                bool found = FindCandidate(target, arr, odd, oddIndex[i], ref iterations) || 
                    FindCandidate(target, arr, even, evenIndex[i], ref iterations);
                if (found)
                    return iterations;
            }
            return iterations;
        }
        static IEnumerable<string> PowerSet(int n)
        {
            for (long i = (1L << (n-1)); i < (1L << n); i++)
            {
                yield return Convert.ToString(i, 2).PadLeft(n, '0');
            }
        }
        static void Main(string[] args)
        {
            for (int i = 5; i < 64; i++)
            {
                int c = 0;
                string hardest_string = "";
                foreach (string s in PowerSet(i))
                {
                    int cost = find_spaced_ones(s);
                    if (cost > c)
                    {
                        hardest_string = s;
                        c = cost;
                        Console.Write("{0} {1} {2}\r", i, c, hardest_string);
                    }
                }
                Console.WriteLine("{0} {1} {2}", i, c, hardest_string);
            }
        }
    }
}

主な違いは次のとおりです。

  1. 解の網羅的検索
    このコードは、データの強力なセットを生成して、このアルゴリズムで解くのが最も難しい入力を見つけます。
  2. すべてのソリューションと解決が最も難しい
    前の質問のコードは、Pythonジェネレーターを使用してすべてのソリューションを生成しました。このコードは、各パターン長で最も難しいものを表示するだけです。
  3. スコアリングアルゴリズム
    このコードは、中央の要素からその左端および右端までの距離をチェックします。Pythonコードは、合計が0より大きいか小さいかをテストしました。
  4. 候補
    の収束現在のコードは、候補を見つけるために中央から端に向かって機能します。前の問題のコードは、端から中央に向かって機能しました。この最後の変更により、パフォーマンスが大幅に向上します。
  5. 偶数および奇数のプールの使用
    この記述の最後の観察に基づいて、コードは偶数のペアと奇数のペアを検索して、LとUを見つけ、Mを固定します。これにより、情報を事前に計算することにより、検索の数を減らします。したがって、コードはFindCandidateのメインループで2レベルの間接参照を使用し、中間要素ごとにFindCandidateを2回呼び出す必要があります。偶数に対して1回と奇数に対して1回です。

一般的なアイデアは、データの生の表現ではなく、インデックスに取り組むことです。1が現れる配列を計算すると、アルゴリズムは、データの長さに比例する時間ではなく、データ内の1の数に比例する時間で実行できます。これは標準的な変換です。問題を同等に保ちながら、より高速な操作を可能にするデータ構造を作成します。

結果は古くなっています:削除されました。


編集:2009-10-16 18:48

yxのデータでは、他の応答で、計算するのに難しいデータの代表としていくつかの信用が与えられていますが、これらの結果が得られます...これらを削除しました。彼らは時代遅れです。

このデータは私のアルゴリズムにとって最も難しいものではないことを指摘します。そのため、yxのフラクタルは解くのが最も難しいという仮定は間違っています。特定のアルゴリズムの最悪のケースは、アルゴリズム自体に依存し、異なるアルゴリズム間で一貫性がない可能性が高いと思います。


編集:2009-10-17 13:30

これに関するさらなる観察。

まず、0と1の文字列を1の各位置のインデックスの配列に変換します。その配列Aの長さがXであるとします。次に、目的は

0 <= L < M < U <= X-1

そのような

A[M] - A[L] = A[U] - A[M]

または

2*A[M] = A[L] + A[U]

A [L]とA [U]は合計が偶数であるため、(偶数、奇数)または(奇数、偶数)にすることはできません。一致の検索は、A []を奇数と偶数のプールに分割し、奇数と偶数の候補のプールでA [M]の一致を順に検索することで改善できます。

ただし、これはアルゴリズムの改善というよりはパフォーマンスの最適化の方が多いと思います。比較の数は減りますが、アルゴリズムの順序は同じでなければなりません。


編集2009-10-18 00:45

候補者を偶数と奇数に分けるのと同じように、私にはさらに別の最適化が起こります。3つのインデックスを3の倍数に追加する必要があるため(a、a + x、a + 2x-aとxに関係なくmod 3は0)、L、M、Uをmod 3の値に分離できます:

M  L  U
0  0  0
   1  2
   2  1
1  0  2
   1  1
   2  0
2  0  1
   1  0
   2  2

実際、これを偶数/奇数の観測と組み合わせて、それらをmod 6の値に分けることができます。

M  L  U
0  0  0
   1  5
   2  4
   3  3
   4  2
   5  1

等々。これは、さらなるパフォーマンスの最適化を提供しますが、アルゴリズムのスピードアップは提供しません。


2

まだ解決策を思い付くことができませんでした:(しかし、いくつかのアイデアがあります。

逆の問題から始めるとどうなるでしょうか。1の最大数で、等間隔に配置されたトリオなしでシーケンスを作成します。1の最大数がo(n)であることを証明できる場合は、1のリストのみを反復処理することにより、推定を改善できます。


まあ、1の数は確かにO(n)によって制限されます。O(n ** 2)にすることはできません。1の数はデータよりも速く増加しますか?重要な問題は、上限がそれよりも低いかどうかです。
hughdbrown、2009年

私は大きなoではなく小さなoを使用しました
Olexiy

2

これは役立つかもしれません...

この問題は次のように減少します。

正の整数のシーケンスを前提として、サブシーケンスのプレフィックスの合計がサブシーケンスのサフィックスの合計と等しくなるように、プレフィックスとサフィックスに分割された連続したサブシーケンスを見つけます。

たとえば、のシーケンスが与えられた場合、のプレフィックスとのプレフィックスの合計とサフィックスのの[ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ]サブシーケンスが見つかります[ 3, 6, 5, 2, 2][ 3, 6 ]9[ 5, 2, 2 ]の接尾辞和とを9

削減は次のとおりです。

0と1のシーケンスが与えられ、左端のシーケンスから開始して、右に移動し続けます。別の移動が発生するたびに、前の移動が発生してからの移動数を記録し、その数を結果のシーケンスに追加します。

たとえば、一連の [ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]、の減少が見つかります[ 1, 3, 4]。この削減から、の連続したサブシーケンス[ 1, 3, 4]、の[ 1, 3]合計のプレフィックス4、およびの[ 4 ]合計のサフィックスを計算します4ます。

この削減は、 O(n)

残念ながら、私はここからどこへ行くべきかわかりません。


1
これはより簡潔な表記ですが、時間の複雑さには役立ちません。「プレフィックス」パーティションのセットは、「1」のすべてのオカレンス(O(n ^ 2))でのすべてのペアの検索と同型です。
-p00ya

隣接するサブシーケンスの合計を処理するアルゴリズムは、明らかにそこにあります。残念ながら、それらはすべて、O(n)で最大の合計を持つ隣接する部分列を見つけることに対処しているようです。
yfeldblum 2009年

@ p00yaこれは正しくありません。このアルゴリズムを使用すると、時間の複雑さは、偽の数の上限に依存します。これは、Cantorで生成された文字列のアスパトンによって((3/2)^(log(n)/ log(3)))であり、スペースの複雑さはこれになります。しかし、時間の複雑さは、このn倍になります。2番目の答えを確認してください。(ネガティブなものではありません):D
ルカ・ラーン

@ralu:これは、Cantorが生成した文字列が最悪の場合であるというあなたの仮定のもとにありますが、これは誤りです。記録では、ペアの数は確かにO(n ^ 2)です。しかし、私はそれがbig-Omega(n ^ 2)であったことを本当に暗示していたと思います。これらの結果(特にNrootNリンクを参照)を考えると正しくありません。big-Omega(n ^(2 / 1.52 ))証明によるか、予想によるbig-Omega(n ^(4/3))。
-p00ya

1

単純な問題タイプの場合(つまり、「1」検索して(つまり0以上)「0」のみ)、非常に単純です。シーケンスを「1」ごとに分割し、次の2つの隣接するサブシーケンスを探すことができます。同じ長さです(もちろん、2番目のサブシーケンスは最後のものではありません)。明らかに、これはO(n)で実行できます時間。

より複雑なバージョン(つまり、インデックスiとギャップg > 0 を検索するなどs[i]==s[i+g]==s[i+2*g]=="1")の場合、O(n log n)ソリューションが存在するかどうかはわかりません。O(n²)トリプレットがこのプロパティ(すべて1の文字列を考えると、およそn²/ 2のようなトリプレットがあります)。もちろん、あなたはこれらの1つだけを探していますが、私は現在それを見つける方法を知りません...


はい、問題のより難しいバージョンについて議論しています。それでも、n * log(n)ソリューションは可能かもしれません。
Olexiy 2009年

1
O(n ^ 3)可能なトリプルであるn choose 3が実際にあります。おおよそn ^
2/2

@gmatt:n select 2で十分です。2つの1を修正すると、3番目の位置が決定され、その位置に1があるかどうかを確認するための定数時間になります。
ShreevatsaR

@ShreevatsaR:そうだね、そうだね、私は無制限のケースを考えていた。
ldog 2009年

1
@gmatt:実際には、0 <= i <(n-3)および0 <g <(ni-1)/ 2の制約を持つ上記で定義されたタプル(i、g)を探しているため、 n ^
2/2

1

面白い質問ですが、2つの「1」の間の実際のパターンが重要ではないとわかったら、アルゴリズムは次のようになります。

  • 「1」のスキャンルック
  • 次の位置スキャンから開始して、別の「1」をスキャンします(配列の最後から現在の最初の「1」からの距離を差し引いたものでなければ、3番目の「1」は範囲外になります)。
  • 2番目の「1」の位置に最初の1 'までの距離に3番目の「1」を加えた位置が見つかった場合、等間隔になります。

コードでは、JTest方式です(このコードは最も効率的になるように作成されていないため、何が起こるかを確認するためにprintlnをいくつか追加しました)。

import java.util.Random;

import junit.framework.TestCase;

public class AlgorithmTest extends TestCase {

 /**
  * Constructor for GetNumberTest.
  *
  * @param name The test's name.
  */
 public AlgorithmTest(String name) {
  super(name);
 }

 /**
  * @see TestCase#setUp()
  */
 protected void setUp() throws Exception {
  super.setUp();
 }

 /**
  * @see TestCase#tearDown()
  */
 protected void tearDown() throws Exception {
  super.tearDown();
 }

 /**
  * Tests the algorithm.
  */
 public void testEvenlySpacedOnes() {

  assertFalse(isEvenlySpaced(1));
  assertFalse(isEvenlySpaced(0x058003));
  assertTrue(isEvenlySpaced(0x07001));
  assertTrue(isEvenlySpaced(0x01007));
  assertTrue(isEvenlySpaced(0x101010));

  // some fun tests
  Random random = new Random();

  isEvenlySpaced(random.nextLong());
  isEvenlySpaced(random.nextLong());
  isEvenlySpaced(random.nextLong());
 }

 /**
  * @param testBits
  */
 private boolean isEvenlySpaced(long testBits) {
  String testString = Long.toBinaryString(testBits);
  char[] ones = testString.toCharArray();
  final char ONE = '1';

  for (int n = 0; n < ones.length - 1; n++) {

   if (ONE == ones[n]) {
    for (int m = n + 1; m < ones.length - m + n; m++) {

     if (ONE == ones[m] && ONE == ones[m + m - n]) {
      System.out.println(" IS evenly spaced: " + testBits + '=' + testString);
      System.out.println("               at: " + n + ", " + m + ", " + (m + m - n));
      return true;
     }
    }
   }
  }

  System.out.println("NOT evenly spaced: " + testBits + '=' + testString);
  return false;
 }
}

4
私が間違っていなければ、これはO(n²)です。これは、外側のループがn回実行され、内側のループが平均でn / 2回実行されるためです。
StriplingWarrior

外側のループはn回実行され、内側のループは平均でn / 4実行されますが、「1」に続く位置からのみ開始されます。n ^ 2の動作に近づくには、「1」の数を多くする必要があります。これにより、真の結果が早期に得られ、処理が停止します。したがって、n ^ 2の動作は発生しません。データの既知の特性に基づいてOを決定する方法は、今のところ私にはありません。
rsp

残念ながら、それは平均的な実際のランタイムではなく、理論上のBig Oランタイムについてのものです。そしてあなたのアプローチはO(n²)です(あなたのアプローチは私のものと同じなので私のものと同じです)
DaClown

私は平均的な行動ではなく、最大の行動について話していました。テストに失敗した最大エントロピーの文字列にlog n '1'が含まれていることが証明されても、私は驚かないでしょう。
rsp

外側のループのインデックスを内側のループで見つかった最初の1のインデックスで更新する場合、つまり(ones [m] == ONE){n = m}の場合はどうなるでしょうか。それはビッグオーを助けますか?
蒸し器

1

うまくいくかもしれない分割統治法を考えました。

まず、前処理では、入力サイズの半分(n / 3)未満のすべての数値をリストに挿入する必要があります。

文字列が与えられた場合:(0000010101000100この特定の例は有効であることに注意してください)

1から(16/2)までのすべての素数(および1)をリストに挿入:{1、2、3、4、5、6、7}

次にそれを半分に分けます:

100000101 01000100

サイズ1の文字列に到達するまでこれを繰り返します。1が含まれるすべてのサイズ1の文字列について、可能性のあるリストに文字列のインデックスを追加します。それ以外の場合、失敗した場合は-1を返します。

また、各開始インデックスに関連付けられた、可能なスペーシング距離のリストを返す必要もあります。(上で作成したリストから始め、数字を削除していきます)ここで、空のリストは1つの1のみを処理することを意味するため、この時点では任意のスペースを使用できます。それ以外の場合、リストには除外する必要のあるスペースが含まれます。

上記の例を続けます:

1000 0101 0100 0100

10 00 01 01 01 00 01 00

1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0

最初の結合ステップでは、2つのセットが8つあります。最初は、セットの可能性がありますが、他のゼロが存在するため、1の間隔を空けることは不可能であることがわかります。したがって、0(インデックスの場合)と{2,3,4,5,7}を返します。これは、1だけスペースを空けることができないためです。2番目では、何もないので-1を返します。3番目では、インデックス5でスペースが削除されていないため、5を返します{1,2,3,4,5,7}。4番目のペアでは、7、{1,2,3,4,5,7}を返します。5番目では、9を返します{1,2,3,4,5,7}。6番目に、-1を返します。7番目に、13を返します{1,2,3,4,5,7}。8番目に、-1を返します。

再び4つの4つのセットに組み合わせると、次のようになります。

1000:戻る(0、{4,5,6,7}) 0101:Return(5、{2,3,4,5,6,7})、(7、{1,2,3,4,5,6 、7}) 0100:リターン(9、{3,4,5,6,7}) 0100:Return(13、{3,4,5,6,7})

8つのセットに組み合わせる:

10000101:(0、{5,7})、(5、{2,3,4,5,6,7})、(7、{1,2,3,4,5,6,7})を返す 01000100ます: (9、{4,7})を返す(13、{3,4,5,6,7})

16のセットに組み合わせる:

10000101 01000100

私たちが進歩するにつれて、私たちはこれまですべての可能性をチェックし続けます。このステップまでは、文字列の終わりを超えるものを残しましたが、これですべての可能性を確認できます。

基本的に、最初の1を5と7の間隔でチェックし、それらが1に揃っていないことがわかります。(各チェックは定数であり、線形時間ではないことに注意してください)次に、2、3、4、5、6、7の間隔で2番目のチェック(インデックス5)をチェックします。それは実際に一致します。

ふew!これはかなり長いアルゴリズムです。

最後のステップのため、O(n log n)かどうかは100%わかりませんが、それまでのすべてが確実にO(n log n)です。です。私は後でこれに戻り、最後のステップを改良しようとします。

編集:ウェルボグのコメントを反映するように私の答えを変更しました。エラーでごめんなさい。私が後で書いたものを解読するためにもう少し時間があるときにも、後でいくつかの疑似コードを書きます。;-)


私はあなたのアルゴリズムには従いませんが、実際にO(n log n)になろうとするアルゴリズムを試すための+1
ldog

ありがとう。時間が増えたら、それをよりよく説明しようと思います(おそらく、いくつかの疑似コードなどを記述します)。
Platinum Azure、

なぜ素数のギャップの可能性のみを検討しているのですか?次のような文字列に一致することをどのように提案し100010001ますか?私があなたのアプローチを正しく理解している場合、正しい答え(0,{4})を計算することができないため、それを一致させることはできません。リストに非素数が必要な場合、チェックする必要のある可能性のリストをO(n log(n))よりも高くする病理学的文字列を思いつくのは簡単だと思います。
ウェルボグ、2009年

誓うまあ、私はもともと倍数をするつもりでしたが、私は一種の貫通私の答えを途中で変更して、すべてを変えるには取得できませんでした。ごめんなさい。まもなく修正されます
プラチナAzure

3
O(n log n)ではないと思います。最初の結合ステップでは、(n / 2)セットを処理します。各セットはO(n)可能な間隔のセットを返す可能性があります。残念ながら、これだけではO(n ^ 2)になります。
MartinStettner、2009年

1

ここでは大まかな推測をします。複雑さの計算が上手な人に、アルゴリズムがO表記法でどのように機能するかを教えてもらいます

  1. 与えられたバイナリ文字列0000010101000100(例として)
  2. ゼロの頭と尾をトリミング-> 00000 101010001 00
  3. 以前の計算から101010001を取得します
  4. 真ん中のビットが「1」であるかどうかをチェックし、trueの場合、有効な3つの等間隔の「1」を見つけました(ビット数が奇数の場合のみ)
  5. 相対的に、切り取られた残りのビット数が偶数の場合、先頭と末尾の「1」を等間隔の「1」の一部にすることはできません。
  6. 例として1010100001を使用します(偶数のクロップになるように「ゼロ」を追加します)。この場合、再度クロップする必要があり、その後-> 10101 00001になります。
  7. 前の計算から10101を取得し、中央のビットをチェックすると、等間隔のビットが再び見つかりました

これの複雑さを計算する方法がわかりません、誰か助けてもらえますか?

編集:私のアイデアを説明するためにいくつかのコードを追加します

edit2:コードをコンパイルしようとしたところ、いくつかの重大な誤りが見つかりました。修正しました

char *binaryStr = "0000010101000100";

int main() {
   int head, tail, pos;
   head = 0;
   tail = strlen(binaryStr)-1;
   if( (pos = find3even(head, tail)) >=0 )
      printf("found it at position %d\n", pos);
   return 0;
}

int find3even(int head, int tail) {
   int pos = 0;
   if(head >= tail) return -1;
   while(binaryStr[head] == '0') 
      if(head<tail) head++;
   while(binaryStr[tail] == '0') 
      if(head<tail) tail--;
   if(head >= tail) return -1;
   if( (tail-head)%2 == 0 && //true if odd numbered
       (binaryStr[head + (tail-head)/2] == '1') ) { 
         return head;
   }else {
      if( (pos = find3even(head, tail-1)) >=0 )
         return pos;
      if( (pos = find3even(head+1, tail)) >=0 )
         return pos;
   }
   return -1;
}

@recursive find3even(head + 1、tail)の呼び出しに達したときに機能すると思います。これにより、head = 4で111になるようにトリミングされます。もう一度確認してください。
andycjw 2009年

@recursive追加したコードを確認して、以前に作成した疑似コードをより詳しく説明してください。これはそれほど厳密で簡潔ではありません
andycjw

これはnlognです。nビットの場合、Cが定数であるn * cビットをチェックするおよそlogn回の反復が期待されます。
ロンウォーリック

ええ、これは111001と100111で最も単純なケースとして失敗するようです。等間隔の1は、中央のビットを中心にする必要はありません。
Dean J

これらのケースを正しく処理します。111001のビット数は偶数であるため、111と001にすぐに分割されます。111のビット数が奇数で、中央のビットが1であるため、正常に返されます。
ロンウォーリック

1

私はこのようなものを思いつきました:

def IsSymetric(number):
    number = number.strip('0')

    if len(number) < 3:
        return False
    if len(number) % 2 == 0:
        return IsSymetric(number[1:]) or IsSymetric(number[0:len(number)-2])
    else:
        if number[len(number)//2] == '1':
            return True
        return IsSymetric(number[:(len(number)//2)]) or IsSymetric(number[len(number)//2+1:])
    return False

これはandycjwに触発されました。

  1. ゼロを切り捨てます。
  2. 場合でも、2つの部分文字列0-(len-2)(最後の文字をスキップ)と1-(len-1)(最初の文字をスキップ)をテストします
  3. 真ん中のcharが1である場合でも、成功している場合とは異なります。それ以外の場合は、midle要素なしでmidleの文字列を分割し、両方の部分を確認します。

複雑さに関しては、これはO(nlogn)になる可能性があります。これは、各再帰で2で除算しているためです。

それが役に立てば幸い。


N要素の問題をN-1要素の2つの問題に変換しているようです。半分に分割すると、N / 2要素の2つの問題に変換されます。
RHSeeger 2009年

それは長さが偶数の場合にのみ当てはまります。したがって、lenが8の場合、アルゴリズムは長さが7、7、3、3、3、3、3の文字列を作成します。再帰ツリーの高さは3で、これはlg(8)と等しくなります。
Beku

1

わかりました、問題をもう一度試します。1の間の距離を格納するためにバランスのとれたバイナリツリーを使用することで、すでに説明したものと同様のO(n log(n))アルゴリズムを証明できると思います。このアプローチは、問題を1の間の距離のリストに減らすというジャスティスの観察に触発されました。

各ノードが1の位置を格納し、各エッジが各子ノードの隣接する1までの距離でラベル付けされるように、入力文字列をスキャンして1の位置の周りにバランスのとれたバイナリツリーを構築できますか?例えば:

10010001 gives the following tree

      3
     / \
  2 /   \ 3
   /     \
  0       7

これは、O(n log(n))で実行できます。これは、サイズnの文字列の場合、挿入ごとに最悪の場合O(log(n))が必要になるためです。

次に問題は、ツリーを検索して、任意のノードで、そのノードから左の子を通るパスが右の子を通るパスと同じ距離にあるかどうかを見つけることです。これは、各サブツリーで再帰的に実行できます。検索で2つのサブツリーをマージする場合、左側のサブツリーのパスからの距離と右側のパスからの距離を比較する必要があります。サブツリー内のパスの数はlog(n)に比例し、ノードの数はnであるため、これはO(n log(n))時間で実行できると思います。

私は何かを逃しましたか?


「サブツリー内のパスの数はlog(n)に比例するため」なぜnではないのですか?一般に、これは有望なアプローチです。
sdcvvc 2009年

@sdcwc:バランスツリーでは各サブツリーにノードの半分があり、サブツリーのルートへのパスの数はサブツリーのノードの数と同じであるため、nではなく、log(n)に比例します(ただし、ルート)。
Jeremy Bourque、

0

これは楽しい問題のように思えたので、私はそれを試してみることにしました。

私は、111000001が最初の3つを見つけて成功すると想定しています。定義によれば、0111000は111000と同じであるため、基本的に1に続くゼロの数は重要です。1の2つのケースが見つかったら、次に見つかった1が三部作を完成させます。

ここではPythonでです:

def find_three(bstring):
    print bstring
    dict = {}
    lastone = -1
    zerocount = 0
    for i in range(len(bstring)):
        if bstring[i] == '1':
            print i, ': 1'
            if lastone != -1:
                if(zerocount in dict):
                    dict[zerocount].append(lastone)
                    if len(dict[zerocount]) == 2:
                        dict[zerocount].append(i)
                        return True, dict
                else:
                    dict[zerocount] = [lastone]
            lastone = i
            zerocount = 0
        else:
            zerocount = zerocount + 1
    #this is really just book keeping, as we have failed at this point
    if lastone != -1:
        if(zerocount in dict):
            dict[zerocount].append(lastone)
        else:
            dict[zerocount] = [lastone]
    return False, dict

これは最初の試みなので、これはより簡潔な方法で書くことができると確信しています。このメソッドが失敗するケースを以下にリストしてください。


@recursive、それらは等間隔ではありません。
James McMahon、

等間隔とはどういう意味ですか?インデックス0、3、および6を見てください。すべて1で、2つずつ分離しています。
再帰的

ああ、わかりましたが、ゼロはスペースにのみ含まれていました。
James McMahon、

質問には、これが機能しない「1001011」が含まれています。以前と同じ(現在は削除済み)の回答があり、質問が出された直後に投稿されました。これにより、この問題と同じ(その他の)問題が解決されました。:-)
ShreevatsaR

今日私は仕事でこれを見ていました、そして、ロブが彼の編集で何を意味するのか理解できませんでした。わかりやすくするために質問を編集しました。私はそれで簡単に時間を過ごしたときに何かが足りなかったことを知っているべきでした。
ジェームズマクマホン

0

これがnlog(n)である理由は、次が原因であると思います。

  • トリプレットの開始である1を見つけるには、(n-2)文字をチェックする必要があります。その時点までにそれが見つからない場合は、(chars n-1およびnはトリプレットを開始することはできません)(O(n))
  • (最初のものから始まる)トリプレットの一部である2番目の1を見つけるには、m / 2(m = nx、xは最初の1のオフセット)文字をチェックする必要があります。これは、最初の1番目から最後までの途中で2番目の1が見つからなかった場合、見つからないためです。(O(ログ(n)))
  • 最初と2番目を見つけるまでにインデックスがなければならないので、最後の1を見つけるのはO(1)です。

したがって、n、log(n)、および1 ... O(nlogn)があります。

編集:おっと、私の悪い。私の脳は、n / 2がlognであると設定していました...これは明らかにそうではありません(アイテムの数を2倍にしても、内部ループの反復数は2倍になります)。これはまだn ^ 2で、問題を解決していません。まあ、少なくとも私はいくつかのコードを書く必要がありました:)


Tclでの実装

proc get-triplet {input} {
    for {set first 0} {$first < [string length $input]-2} {incr first} {
        if {[string index $input $first] != 1} {
            continue
        }
        set start [expr {$first + 1}]
        set end [expr {1+ $first + (([string length $input] - $first) /2)}]
        for {set second $start} {$second < $end} {incr second} {
            if {[string index $input $second] != 1} {
                continue
            }
            set last [expr {($second - $first) + $second}]
            if {[string index $input $last] == 1} {
                return [list $first $second $last]
            }
        }
    }
    return {}
}

get-triplet 10101      ;# 0 2 4
get-triplet 10111      ;# 0 2 4
get-triplet 11100000   ;# 0 1 2
get-triplet 0100100100 ;# 1 4 7

0

私は問題を解決する方法を見つけたと思いますが、正式な証明を構築することはできません。私が作成したソリューションはJavaで記述されており、カウンター「n」を使用して、それが行うリスト/配列アクセスの数をカウントします。したがって、nが正しい場合は、stringLength * log(stringLength)以下にする必要があります。0から2 ^ 22までの数値で試してみましたが、うまくいきました。

まず、入力文字列を繰り返し処理し、1を保持するすべてのインデックスのリストを作成します。これは単なるO(n)です。

次に、インデックスのリストから、firstIndexと、firstよりも大きいsecondIndexを選択します。これらの2つのインデックスはインデックスのリストにあるため、インデックスを保持する必要があります。そこから、thirdIndexを計算できます。inputString [thirdIndex]が1の場合、停止します。

public static int testString(String input){
//n is the number of array/list accesses in the algorithm
int n=0;

//Put the indices of all the ones into a list, O(n)
ArrayList<Integer> ones = new ArrayList<Integer>();
for(int i=0;i<input.length();i++){
    if(input.charAt(i)=='1'){
        ones.add(i);
    }
}

//If less than three ones in list, just stop
if(ones.size()<3){
    return n;
}

int firstIndex, secondIndex, thirdIndex;
for(int x=0;x<ones.size()-2;x++){
    n++;
    firstIndex = ones.get(x);

    for(int y=x+1; y<ones.size()-1; y++){
        n++;
        secondIndex = ones.get(y);
        thirdIndex = secondIndex*2 - firstIndex;

        if(thirdIndex >= input.length()){
            break;
        }

        n++;
        if(input.charAt(thirdIndex) == '1'){
            //This case is satisfied if it has found three evenly spaced ones
            //System.out.println("This one => " + input);
            return n;
        }
    }
}

return n;

}

追加の注記:インデックスのリストを構築するために入力文字列を繰り返し処理しても、カウンターnは増加しません。この操作はO(n)であるため、アルゴリズムの複雑度には影響しません。


まだO(n)の2つのループがネストされているように見えるため、O(n ^ 2)になります
RHSeeger

インデックスの配列は、入力文字列と同じサイズではありません。これは、私が本当の証明を書いたり、それが正しくないことを証明したりするのを難しくしています。これを機能させるいくつかの根本的な数学的アイデアがあると思います。
ロバートパーカー、

1
この問題の秘訣は、アルゴリズムがO(n ^ 2)であるにもかかわらず、取得できる文字列の最悪のケースではO(nlogn)の反復のみが発生し、それ以外の場合はアルゴリズムを使用した解決策が見つかることです。
z-

2
2 ^ 22までテストしても、複雑さは実際にはテストされません。2 ^ 22は22ビットしかありません。つまり、Nは22です。Nが数百万であるいくつかの値で試してください。
ピーター・レコア

1
yxの回答で与えられた最大の「悪い」文字列の1つでこのアルゴリズムを試してみてください。これがアルゴリズムであることがわかりO(n^2)ます。
ウェルボグ2009年

0

問題への一つの入り込みは、要因とシフトを考えることです。

シフトでは、1と0の文字列をそれ自体のシフトバージョンと比較します。次に、一致するものを取ります。この例を2つシフトしてみましょう。

1010101010
  1010101010
------------
001010101000

結果の1(ビットごとのAND演算)は、2で等間隔に配置されたすべての1を表す必要があります。同じ例が3つずれています。

1010101010
   1010101010
-------------
0000000000000

この場合、3等間隔の1はありません。

これは何を意味しますか?素数であるシフトをテストする必要があるだけです。たとえば、6つ離れている2つの1があるとします。'2'シフトと '3'シフトをテストするだけで済みます(これらは6を分割するため)。例えば:

10000010 
  10000010 (Shift by two)
    10000010
      10000010 (We have a match)

10000010
   10000010 (Shift by three)
      10000010 (We have a match)

したがって、確認する必要がある唯一のシフトは、2、3、5、7、11、13などです。数字列のサイズの平方根に最も近い素数まで。

ほぼ解決しましたか?

私は解決策に近いと思います。基本的に:

  1. 文字列を1でスキャンします。各1音について、その位置の係数をとった後の残りです。係数の範囲は、1から文字列のサイズの半分です。これは、可能な最大の分離サイズが文字列の半分であるためです。これはO(n ^ 2)で行われます。だが。チェックする必要があるのは素数係数のみなので、O(n ^ 2 / log(n))
  2. 最初に係数/剰余のリストを最大係数の順に並べ替えます。これはO(n * log(n))時間で実行できます。
  3. 同じである3つの連続する係数/剰余を探します。
  4. どういうわけかそれらの位置を取得します!

答えの最大の手がかりは、最速のソートアルゴリズムがO(n * log(n))であることだと思います。

違う

同僚が指摘したように、ステップ1は間違っています。位置2、12、102に1がある場合、係数を10にすると、余りはすべて同じになりますが、等間隔ではありません。ごめんなさい。


これは興味深いアプローチです。完全なソリューションを思いついた場合はお知らせください。
James McMahon、

数値kをO(n)回シフトし、シフトごとにO(n)チェックを行うと、1つの数値だけシフトした場合でも、O(n ^ 2)アルゴリズムが生成されます。アルゴリズムは複数の数値でシフトする必要があります。
ldog 2009年

0

私の最善の努力にもかかわらず、お辞儀をしているように思われないいくつかの考えがあります。それでも、それらは誰かの分析の出発点として役立つかもしれません。

提案された解決策を次のように検討してください。これは、この回答の以前のバージョンに私も含めて、何人かの人々が提案したアプローチです。 :)

  1. 先頭と末尾のゼロを削除します。
  2. 文字列をスキャンして1を探します。
  3. 1が見つかった場合:
    1. それが解の真ん中の1であると仮定します。
    2. 以前の各1について、その保存された位置を使用して、最後の1の予想される位置を計算します。
    3. 計算された位置が文字列の末尾の後ろにある場合は、それを解の一部にすることはできないため、候補のリストから位置を削除します。
    4. 解決策を確認してください。
  4. 解が見つからなかった場合は、現在の1を候補のリストに追加します。
  5. 1が見つからなくなるまで繰り返します。

次に、次のような入力文字列の文字列を考えます。これには解決策がありません。

101
101001
1010010001
101001000100001
101001000100001000001

一般に、これは、j 0の形式のk個の文字列とそれに続く0からk-1までのjの1の連結です。

k=2  101
k=3  101001
k=4  1010010001
k=5  101001000100001
k=6  101001000100001000001

サブストリングの長さは1、2、3などであることに注意してください。したがって、問題サイズnには、n = k(k + 1)/ 2となる長さ1からkのサブストリングがあります。

k=2  n= 3  101
k=3  n= 6  101001
k=4  n=10  1010010001
k=5  n=15  101001000100001
k=6  n=21  101001000100001000001

kは、考慮しなければならない1の数も追跡することに注意してください。1が表示されるたびに、これまでに表示された1をすべて考慮する必要があることに注意してください。したがって、2番目の1を見るときは最初の1つだけを考慮し、3番目の1を見るときは最初の2つを再考し、4番目の1を見るときは最初の3つを再考する必要があります。アルゴリズムの終わりまでに、1のk(k-1)/ 2ペアを検討しました。そのpを呼び出します。

k=2  n= 3  p= 1  101
k=3  n= 6  p= 3  101001
k=4  n=10  p= 6  1010010001
k=5  n=15  p=10  101001000100001
k=6  n=21  p=15  101001000100001000001

nとpの関係は、n = p + kです。

文字列を通過するプロセスにはO(n)時間かかります。1に出会うたびに、最大(k-1)回の比較が行われます。n = k(k + 1)/ 2なので、n> k ** 2なので、sqrt(n)> kです。これにより、O(n sqrt(n))またはO(n ** 3/2)が得られます。ただし、比較の数が1から最大kになるため、それは実際には厳密な制限ではない可能性があることに注意してください。しかし、それを数学でどのように説明するのかわかりません。

それはまだO(n log(n))ではありません。また、これらの入力が最悪のケースであることを証明することはできません。前に1を密に配置すると、最後にさらに疎なパッキングになると思います。

誰かがまだ便利だと思うかもしれないので、Perlでのその解決策の私のコードを次に示します。

#!/usr/bin/perl

# read input as first argument
my $s = $ARGV[0];

# validate the input
$s =~ /^[01]+$/ or die "invalid input string\n";

# strip leading and trailing 0's
$s =~ s/^0+//;
$s =~ s/0+$//;

# prime the position list with the first '1' at position 0
my @p = (0);

# start at position 1, which is the second character
my $i = 1;

print "the string is $s\n\n";

while ($i < length($s)) {
   if (substr($s, $i, 1) eq '1') {
      print "found '1' at position $i\n";
      my @t = ();
      # assuming this is the middle '1', go through the positions
      # of all the prior '1's and check whether there's another '1'
      # in the correct position after this '1' to make a solution
      while (scalar @p) {
         # $p is the position of the prior '1'
         my $p = shift @p;
         # $j is the corresponding position for the following '1'
         my $j = 2 * $i - $p;
         # if $j is off the end of the string then we don't need to
         # check $p anymore
         next if ($j >= length($s));
         print "checking positions $p, $i, $j\n";
         if (substr($s, $j, 1) eq '1') {
            print "\nsolution found at positions $p, $i, $j\n";
            exit 0;
         }
         # if $j isn't off the end of the string, keep $p for next time
         push @t, $p;
      }
      @p = @t;
      # add this '1' to the list of '1' positions
      push @p, $i;
   }
   $i++;
}

print "\nno solution found\n";

「非解決」シーケンスが間違っています。各1のインデックスは、三角形の数字のシーケンス1、3、6、10、15 ...などであり、算術列を形成する数字6、36、および66を含みます。
ジェイソンS

0

1をスキャンするときに、それらの位置をリストに追加します。2番目以降の1を追加するときは、これまでのリストの各位置と比較してください。間隔はcurrentOne(中央)-previousOne(左)と等しくなります。右側のビットはcurrentOne + spacingです。1なら終わり。

1のリストは、それらの間のスペースに反比例して大きくなります。簡単に言うと、1の間に0がたくさんある場合(最悪の場合)、既知の1のリストは非常にゆっくりと増加します。

using System;
using System.Collections.Generic;

namespace spacedOnes
{
    class Program
    {
        static int[] _bits = new int[8] {128, 64, 32, 16, 8, 4, 2, 1};

        static void Main(string[] args)
        {
            var bytes = new byte[4];
            var r = new Random();
            r.NextBytes(bytes);
            foreach (var b in bytes) {
                Console.Write(getByteString(b));
            }
            Console.WriteLine();
            var bitCount = bytes.Length * 8;
            var done = false;
            var onePositions = new List<int>();
            for (var i = 0; i < bitCount; i++)
            {
                if (isOne(bytes, i)) {
                    if (onePositions.Count > 0) {
                        foreach (var knownOne in onePositions) {
                            var spacing = i - knownOne;
                            var k = i + spacing;
                            if (k < bitCount && isOne(bytes, k)) {
                                Console.WriteLine("^".PadLeft(knownOne + 1) + "^".PadLeft(spacing) + "^".PadLeft(spacing));
                                done = true;
                                break;
                            }
                        }
                    }
                    if (done) {
                        break;
                    }
                    onePositions.Add(i);
                }
            }
            Console.ReadKey();
        }

        static String getByteString(byte b) {
            var s = new char[8];
            for (var i=0; i<s.Length; i++) {
                s[i] = ((b & _bits[i]) > 0 ? '1' : '0');
            }
            return new String(s);
        }

        static bool isOne(byte[] bytes, int i)
        {
            var byteIndex = i / 8;
            var bitIndex = i % 8;
            return (bytes[byteIndex] & _bits[bitIndex]) > 0;
        }
    }
}

0

問題の22番目の素朴な解決策を投稿する前に、コメントを1つ追加したいと思いました。素朴なソリューションでは、文字列の1の数が最大でO(log(n))であることを示す必要はありませんが、最大でO(sqrt(n * log(n))であることを示す必要があります。

ソルバー:

def solve(Str):
    indexes=[]
    #O(n) setup
    for i in range(len(Str)):
        if Str[i]=='1':
            indexes.append(i)

    #O((number of 1's)^2) processing
    for i in range(len(indexes)):
        for j in range(i+1, len(indexes)):
                            indexDiff = indexes[j] - indexes[i]
            k=indexes[j] + indexDiff
            if k<len(Str) and Str[k]=='1':
                return True
    return False

基本的にはflybywireのアイデアと実装にかなり似ていますが、後ろではなく前を向いています。

貪欲な文字列ビルダー:

#assumes final char hasn't been added, and would be a 1 
def lastCharMakesSolvable(Str):
    endIndex=len(Str)
    j=endIndex-1
    while j-(endIndex-j) >= 0:
        k=j-(endIndex-j)
        if k >= 0 and Str[k]=='1' and Str[j]=='1':
            return True
        j=j-1
    return False



def expandString(StartString=''):
    if lastCharMakesSolvable(StartString):
        return StartString + '0'
    return StartString + '1'

n=1
BaseStr=""
lastCount=0
while n<1000000:
    BaseStr=expandString(BaseStr)
    count=BaseStr.count('1')
    if count != lastCount:
        print(len(BaseStr), count)
    lastCount=count
    n=n+1

(私の弁護では、私はまだ「Pythonを学ぶ」段階にあります)

また、文字列の貪欲な構築からの潜在的に有用な出力では、1の数で2の累乗を打った後、かなり一貫したジャンプがあります... 2096を打つのを目撃するのを待ちきれませんでした。

strlength   # of 1's
    1    1
    2    2
    4    3
    5    4
   10    5
   14    8
   28    9
   41    16
   82    17
  122    32
  244    33
  365    64
  730    65
 1094    128
 2188    129
 3281    256
 6562    257
 9842    512
19684    513
29525    1024

0

私は数学的なアプローチを提示してみます。これは終わりではなく始まりなので、ヘルプ、コメント、または矛盾さえあります-深く感謝します。ただし、このアプローチが証明されている場合、アルゴリズムは文字列内の単純な検索です。

  1. 固定数のスペースkと文字列が与えられた場合S、k-spaced-tripletの検索には、O(n)すべての0<=i<=(n-2k)ifをテストするだけですS[i]==S[i+k]==S[i+2k]。テストがかかり、定数がどこにあるO(1)かをテストするので、がかかります。n-kkO(n-k)=O(n)

  2. の数1と検索する必要のある最大スペースの間に逆比例があると仮定します。つまり、の数が多い場合は、1トリプレットが存在し、非常に密でなければなりません。の数が少ない場合1、トリプレット(ある場合)は非常にまばらになります。言い換えれば、十分な数1のがある場合、そのようなトリプレットが存在する必要があることを証明できます。また、が多いほど1、より密なトリプレットを見つける必要があります。これはピジョンホールの原則で説明できます。これについては後で詳しく説明します。

  3. k私が探しなければならない可能なスペースの数に上限があると言います。さて、それぞれの1中にあるS[i]私たちのためにチェックする必要がある1S[i-1]S[i+1]S[i-2]そしてS[i+2]、... S[i-k]S[i+k]。これは、かかるO((k^2-k)/2)=O(k^2)1におけるS起因する- ガウスのシリーズ加算式。これはセクション1とは異なることに注意してくださいk。定数スペースとしてではなく、スペース数の上限として使用しています。

証明する必要がありO(n*log(n))ます。つまり、私たちはそれを表示するために必要k*(number of 1's)に比例していますlog(n)ます。

それができれば、アルゴリズムは簡単です。インデックスがそれぞれ1Sある場合はi、単純に1両端から距離までを探しますk。2つが同じ距離で見つかった場合は、iおよびを返しkます。再び、トリッキーな部分は見つけることでしょうk、正しさをて証明することです。

ここにコメントをいただければ幸いです。ホワイトボード上のkとの数の関係を見つけようとしていましたが1、これまでのところ成功していません。


0

仮定:

ちょうど間違っています、1の上限のlog(n)数について話します

編集:

現在、Cantor数を使用して(正しい場合)、セットの密度は(2/3)^ Log_3(n)(奇妙な関数です)であり、log(n)/ n密度は非常に強いことに同意します。

これが上限である場合、少なくともO(n *(3/2)^(log(n)/ log(3)))時間の複雑さとO((3/2)^(でこの問題を解決するアルゴリズムが存在します。 log(n)/ log(3)))スペースの複雑さ。(アルゴリズムの正義の答えを確認してください)

これはO(n ^ 2)よりもはるかに優れています

この関数((3/2)^(log(n)/ log(3)))は、一見するとn * log(n)のように見えます。

どうやってこの公式を手に入れましたか?

文字列にCantors番号を適用しています。
文字列の長さが3 ^ p == n
であると仮定します。Cantor文字列の生成の各ステップで、以前のものの2/3を保持します。これをp回適用します。

つまり、(n *((2/3)^ p))->((((3 ^ p))*((2/3)^ p))が残りの1と簡略化後2 ^ pになります。これは、3 ^ p文字列の2 ^ p個->(3/2)^ p個を意味します。p = log(n)/ log(3)を代入して
((3/2)^(log(n)/ log(3)))を取得


False:Cantorセットの密度はn ^ log_3(2)です。
sdcvvc 2009年

0

O(n ^ 2)スペースを使用した単純なO(n)ソリューションはどうですか?(すべてのビット演算子がO(1)で機能するという仮定を使用します。)

アルゴリズムは基本的に4つの段階で機能します。

ステージ1:元の数値の各ビットについて、ビットがどれだけ離れているかを調べますが、1つの方向のみを考慮してください。(最下位ビットの方向にあるすべてのビットを検討しました。)

ステージ2:入力のビットの順序を逆にします。

ステージ3:反転した入力でステップ1を再実行します。

ステージ4:ステージ1とステージ3の結果を比較します。ビットの上下に等間隔のビットがある場合は、ヒットする必要があります。

上記のアルゴリズムのどのステップもO(n)より長くはかかりません。^ _ ^

追加の利点として、このアルゴリズムはすべての数からすべての等間隔のものを見つけます。たとえば、「0x0005」という結果が得られた場合、1と3の両方のユニットに等間隔のものが存在します。

私は実際には以下のコードを最適化しようとしませんでしたが、動作しているように見えるコンパイル可能なC#コードです。

using System;

namespace ThreeNumbers
{
    class Program
    {
        const int uint32Length = 32;

        static void Main(string[] args)
        {
            Console.Write("Please enter your integer: ");
            uint input = UInt32.Parse(Console.ReadLine());

            uint[] distancesLower = Distances(input);
            uint[] distancesHigher = Distances(Reverse(input));

            PrintHits(input, distancesLower, distancesHigher);
        }

        /// <summary>
        /// Returns an array showing how far the ones away from each bit in the input.  Only 
        /// considers ones at lower signifcant bits.  Index 0 represents the least significant bit 
        /// in the input.  Index 1 represents the second least significant bit in the input and so 
        /// on.  If a one is 3 away from the bit in question, then the third least significant bit 
        /// of the value will be sit.
        /// 
        /// As programed this algorithm needs: O(n) time, and O(n*log(n)) space.  
        /// (Where n is the number of bits in the input.)
        /// </summary>
        public static uint[] Distances(uint input)
        {
            uint[] distanceToOnes = new uint[uint32Length];
            uint result = 0;

            //Sets how far each bit is from other ones. Going in the direction of LSB to MSB
            for (uint bitIndex = 1, arrayIndex = 0; bitIndex != 0; bitIndex <<= 1, ++arrayIndex)
            {
                distanceToOnes[arrayIndex] = result;
                result <<= 1;

                if ((input & bitIndex) != 0)
                {
                    result |= 1;
                }
            }

            return distanceToOnes;
        }

        /// <summary>
        /// Reverses the bits in the input.
        /// 
        /// As programmed this algorithm needs O(n) time and O(n) space.  
        /// (Where n is the number of bits in the input.)
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static uint Reverse(uint input)
        {
            uint reversedInput = 0;
            for (uint bitIndex = 1; bitIndex != 0; bitIndex <<= 1)
            {
                reversedInput <<= 1;
                reversedInput |= (uint)((input & bitIndex) != 0 ? 1 : 0);
            }

            return reversedInput;
        }

        /// <summary>
        /// Goes through each bit in the input, to check if there are any bits equally far away in 
        /// the distancesLower and distancesHigher
        /// </summary>
        public static void PrintHits(uint input, uint[] distancesLower, uint[] distancesHigher)
        {
            const int offset = uint32Length - 1;

            for (uint bitIndex = 1, arrayIndex = 0; bitIndex != 0; bitIndex <<= 1, ++arrayIndex)
            {
                //hits checks if any bits are equally spaced away from our current value
                bool isBitSet = (input & bitIndex) != 0;
                uint hits = distancesLower[arrayIndex] & distancesHigher[offset - arrayIndex];

                if (isBitSet && (hits != 0))
                {
                    Console.WriteLine(String.Format("The {0}-th LSB has hits 0x{1:x4} away", arrayIndex + 1, hits));
                }
            }
        }
    }
}

誰かがおそらく十分に大きな数の場合、ビットごとの演算はO(1)では実行できないとコメントするでしょう。あなたは正しいでしょう。ただし、加算、減算、乗算、または除算(シフトでは実行できない)を使用するすべてのソリューションにも問題があると思います。


0

以下は解決策です。あちこちでいくつかの小さな間違いがあるかもしれませんが、アイデアは健全です。

編集:n * log(n)ではありません

疑似コード:

foreach character in the string
  if the character equals 1 {         
     if length cache > 0 { //we can skip the first one
        foreach location in the cache { //last in first out kind of order
           if ((currentlocation + (currentlocation - location)) < length string)
              if (string[(currentlocation + (currentlocation - location))] equals 1)
                 return found evenly spaced string
           else
              break;
        }
     }
     remember the location of this character in a some sort of cache.
  }

return didn't find evenly spaced string

C#コード:

public static Boolean FindThreeEvenlySpacedOnes(String str) {
    List<int> cache = new List<int>();

    for (var x = 0; x < str.Length; x++) {
        if (str[x] == '1') {
            if (cache.Count > 0) {
                for (var i = cache.Count - 1; i > 0; i--) {
                    if ((x + (x - cache[i])) >= str.Length)
                        break;

                    if (str[(x + (x - cache[i]))] == '1')
                        return true;                            
                }
            }
            cache.Add(x);                    
        }
    }

    return false;
}

使い方:

iteration 1:
x
|
101101001
// the location of this 1 is stored in the cache

iteration 2:
 x
 | 
101101001

iteration 3:
a x b 
| | | 
101101001
//we retrieve location a out of the cache and then based on a 
//we calculate b and check if te string contains a 1 on location b

//and of course we store x in the cache because it's a 1

iteration 4:
  axb  
  |||  
101101001

a  x  b  
|  |  |  
101101001


iteration 5:
    x  
    |  
101101001

iteration 6:
   a x b 
   | | | 
101101001

  a  x  b 
  |  |  | 
101101001
//return found evenly spaced string

1
アルゴリズムは機能しますが、O(n ^ 2)未満であることを証明する必要があります。ささいな分析により、O(n ^ 2)が得られます。各1について、その前にあったすべての1を調べます。複雑度関数を1 + 2 + 3 + ... +(k / 2-1)= O(k ^ 2)[kは1の数]にする。
アンナ

私が確認したところ、実際にソリューションがない最悪のシナリオはO(n * log(n))よりも大きい
Niek H.

0

明らかに、少なくともトリプレットの束を同時にチェックする必要があるため、チェックを何らかの方法で圧縮する必要があります。アルゴリズムの候補がありますが、時間の複雑さの分析は私の能力*時間のしきい値を超えています。

各ノードに3つの子があり、各ノードのリーフに1の総数が含まれるツリーを作成します。1の上にもリンクリストを作成します。各ノードに、それがカバーする範囲に比例する許容コストを割り当てます。各ノードで費やす時間が予算内である限り、O(n lg n)アルゴリズムを使用できます。

-

ルートから始めます。1未満の合計数の2乗が許容コストより少ない場合は、単純なアルゴリズムを適用します。それ以外の場合は、その子を再帰します。

これで予算内に戻ったか、子の1つに完全に含まれる有効なトリプレットがないことがわかりました。したがって、ノード間トリプレットをチェックする必要があります。

今、物事は信じられないほど厄介です。基本的に、範囲を制限しながら、潜在的な子のセットを再帰的に処理します。単純なアルゴリズムが予算以下で実行されるように範囲が十分に制約されるとすぐに、それを実行します。面倒な作業になるので、これを実装してください。ダースケースのようなものがあります。

-

アルゴリズムが機能すると私が思う理由は、有効なトリプレットのないシーケンスが、1の束と0の束の間で交互に移動するように見えるためです。効果的に近くの検索スペースを分割し、ツリーはその分割をエミュレートします。

アルゴリズムの実行時間は明らかではありません。これは、シーケンスの重要なプロパティに依存しています。1が本当にスパースである場合、ナイーブアルゴリズムは予算内で機能します。1が密である場合、一致はすぐに見つかるはずです。しかし、密度が「ちょうどいい」(たとえば、ベース3の「2」桁のない位置にすべてのビットを設定することで達成できる〜n ^ 0.63に近い)場合、それが機能するかどうかはわかりません。分割効果が十分に強いことを証明する必要があります。


0

ここでは理論的な答えはありませんが、実行時の動作をkとnの関数として調査する簡単なJavaプログラムを作成しました。ここで、nは合計ビット長、kは1の数です。最悪の場合、O(k ^ 2)が必要な場合でも、ビット位置のすべてのペアをチェックして3番目のビットを探す「通常の」アルゴリズムは、いくつかの回答者と一緒です。最悪のケースではスパースビット文字列が必要なため、現実はO(n ln n)です。

とにかく、以下がプログラムです。これは、モンテカルロスタイルのプログラムであり、定数nに対して多数の試行NTRIALSを実行し、指定可能な制限間で制約された1密度でベルヌーイプロセスを使用してk値の​​範囲のビットセットをランダムに生成し、実行時間を記録します。 CPU時間ではなく、ステップで測定された時間で、等間隔に配置されたものの3つ組を見つけるか、見つけることに失敗したかを示します。私はそれをn = 64、256、1024、4096、16384 *(まだ実行中)に対して実行し、最初に500000トライアルでテスト実行して、どのk値が最も長い実行時間をとるかを確認し、次に別のテストを5000000トライアルで絞り込みました-これらの値がどのように見えるかを確認するための密度の焦点。最長の実行時間は非常にまばらな密度で発生します(たとえば、n = 4096の場合、実行時間のピークはk = 16-64の範囲にあり、平均実行時間の緩やかなピークは4212ステップ@ k = 31、最大実行時間は5101ステップ@ k = 58でピークに達しました)。最悪の場合のO(k ^ 2)ステップが、ビット文字列をスキャンして1の位置インデックスを見つけるO(n)ステップよりも大きくなるには、Nの値が非常に大きくなるようです。

package com.example.math;

import java.io.PrintStream;
import java.util.BitSet;
import java.util.Random;

public class EvenlySpacedOnesTest {
    static public class StatisticalSummary
    {
        private int n=0;
        private double min=Double.POSITIVE_INFINITY;
        private double max=Double.NEGATIVE_INFINITY;
        private double mean=0;
        private double S=0;

        public StatisticalSummary() {}
        public void add(double x) {
            min = Math.min(min, x);
            max = Math.max(max, x);
            ++n;
            double newMean = mean + (x-mean)/n;
            S += (x-newMean)*(x-mean);
            // this algorithm for mean,std dev based on Knuth TAOCP vol 2
            mean = newMean;
        }
        public double getMax() { return (n>0)?max:Double.NaN; }
        public double getMin() { return (n>0)?min:Double.NaN; }
        public int getCount() { return n; }
        public double getMean() { return (n>0)?mean:Double.NaN; }
        public double getStdDev() { return (n>0)?Math.sqrt(S/n):Double.NaN; } 
        // some may quibble and use n-1 for sample std dev vs population std dev    
        public static void printOut(PrintStream ps, StatisticalSummary[] statistics) {
            for (int i = 0; i < statistics.length; ++i)
            {
                StatisticalSummary summary = statistics[i];
                ps.printf("%d\t%d\t%.0f\t%.0f\t%.5f\t%.5f\n",
                        i,
                        summary.getCount(),
                        summary.getMin(),
                        summary.getMax(),
                        summary.getMean(),
                        summary.getStdDev());
            }
        }
    }

    public interface RandomBernoulliProcess // see http://en.wikipedia.org/wiki/Bernoulli_process
    {
        public void setProbability(double d);
        public boolean getNextBoolean();
    }

    static public class Bernoulli implements RandomBernoulliProcess
    {
        final private Random r = new Random();
        private double p = 0.5;
        public boolean getNextBoolean() { return r.nextDouble() < p; }
        public void setProbability(double d) { p = d; }
    }   
    static public class TestResult {
        final public int k;
        final public int nsteps;
        public TestResult(int k, int nsteps) { this.k=k; this.nsteps=nsteps; } 
    }

    ////////////
    final private int n;
    final private int ntrials;
    final private double pmin;
    final private double pmax;
    final private Random random = new Random();
    final private Bernoulli bernoulli = new Bernoulli();
    final private BitSet bits;
    public EvenlySpacedOnesTest(int n, int ntrials, double pmin, double pmax) {
        this.n=n; this.ntrials=ntrials; this.pmin=pmin; this.pmax=pmax;
        this.bits = new BitSet(n);
    }

    /*
     * generate random bit string
     */
    private int generateBits()
    {
        int k = 0; // # of 1's
        for (int i = 0; i < n; ++i)
        {
            boolean b = bernoulli.getNextBoolean();
            this.bits.set(i, b);
            if (b) ++k;
        }
        return k;
    }

    private int findEvenlySpacedOnes(int k, int[] pos) 
    {
        int[] bitPosition = new int[k];
        for (int i = 0, j = 0; i < n; ++i)
        {
            if (this.bits.get(i))
            {
                bitPosition[j++] = i;
            }
        }
        int nsteps = n; // first, it takes N operations to find the bit positions.
        boolean found = false;
        if (k >= 3) // don't bother doing anything if there are less than 3 ones. :(
        {       
            int lastBitSetPosition = bitPosition[k-1];
            for (int j1 = 0; !found && j1 < k; ++j1)
            {
                pos[0] = bitPosition[j1];
                for (int j2 = j1+1; !found && j2 < k; ++j2)
                {
                    pos[1] = bitPosition[j2];

                    ++nsteps;
                    pos[2] = 2*pos[1]-pos[0];
                    // calculate 3rd bit index that might be set;
                    // the other two indices point to bits that are set
                    if (pos[2] > lastBitSetPosition)
                        break;
                    // loop inner loop until we go out of bounds

                    found = this.bits.get(pos[2]);
                    // we're done if we find a third 1!
                }
            }
        }
        if (!found)
            pos[0]=-1;
        return nsteps;
    }

    /*
     * run an algorithm that finds evenly spaced ones and returns # of steps.
     */
    public TestResult run()
    {
        bernoulli.setProbability(pmin + (pmax-pmin)*random.nextDouble());
        // probability of bernoulli process is randomly distributed between pmin and pmax

        // generate bit string.
        int k = generateBits();
        int[] pos = new int[3];
        int nsteps = findEvenlySpacedOnes(k, pos);
        return new TestResult(k, nsteps); 
    }

    public static void main(String[] args)
    {
        int n;
        int ntrials;
        double pmin = 0, pmax = 1;
        try {
            n = Integer.parseInt(args[0]);
            ntrials = Integer.parseInt(args[1]);
            if (args.length >= 3)
                pmin = Double.parseDouble(args[2]);
            if (args.length >= 4)
                pmax = Double.parseDouble(args[3]);
        }
        catch (Exception e)
        {
            System.out.println("usage: EvenlySpacedOnesTest N NTRIALS [pmin [pmax]]");
            System.exit(0);
            return; // make the compiler happy
        }

        final StatisticalSummary[] statistics;
        statistics=new StatisticalSummary[n+1];
        for (int i = 0; i <= n; ++i)
        {
            statistics[i] = new StatisticalSummary();
        }

        EvenlySpacedOnesTest test = new EvenlySpacedOnesTest(n, ntrials, pmin, pmax);
        int printInterval=100000;
        int nextPrint = printInterval;
        for (int i = 0; i < ntrials; ++i)
        {
            TestResult result = test.run();
            statistics[result.k].add(result.nsteps);
            if (i == nextPrint)
            {
                System.err.println(i);
                nextPrint += printInterval;
            }
        }
        StatisticalSummary.printOut(System.out, statistics);
    }
}

0
# <algorithm>
def contains_evenly_spaced?(input)
  return false if input.size < 3
  one_indices = []
  input.each_with_index do |digit, index|
    next if digit == 0
    one_indices << index
  end
  return false if one_indices.size < 3
  previous_indexes = []
  one_indices.each do |index|
    if !previous_indexes.empty?
      previous_indexes.each do |previous_index|
        multiple = index - previous_index
        success_index = index + multiple
        return true if input[success_index] == 1
      end
    end
    previous_indexes << index
  end
  return false
end
# </algorithm>

def parse_input(input)
  input.chars.map { |c| c.to_i }
end

数百万桁の最悪のシナリオに問題があります。からファジングすると、/dev/urandom基本的にO(n)が得られますが、最悪のケースはそれよりも悪いことはわかっています。どれだけ悪いのかわからない。小規模なn場合、で入力を見つけることは簡単3*n*log(n)ですが、この特定の問題について、他の成長順序と区別するのは驚くほど困難です。

最悪の場合の入力に取り組んでいた人は、言うよりも長い10万の文字列を生成できますか?


私の回答で指摘したように、任意の桁数の悪い(最悪の場合ではない)文字列を生成するのは簡単です。3進数表現(つまり、ポジション2、6、8、18、20、24、26、54、56、60 ...:research.att.com/~njas/sequences/…の式を参照)。3 ^ 13≈100万の場合、これは2 ^ 13≈8000 1になります。そのような文字列の実行時間は≈n ^(1.26)になります—これは、そのような小さなnのO(n log n)と区別するのが難しい場合があります。試してみてください。
ShreevatsaR 09/10/20


-3

これは解決策になるでしょうか?O(nlogn)であるかどうかはわかりませんが、私の意見では、O(n²)よりも優れています。なぜなら、トリプルを見つけない唯一の方法は、素数分布であるためです。

改善の余地があります。2番目に見つかった1は次の最初の1になる可能性があります。エラーチェックもありません。

#include <iostream>

#include <string>

int findIt(std::string toCheck) {
    for (int i=0; i<toCheck.length(); i++) {
        if (toCheck[i]=='1') {
            std::cout << i << ": " << toCheck[i];
            for (int j = i+1; j<toCheck.length(); j++) {
                if (toCheck[j]=='1' && toCheck[(i+2*(j-i))] == '1') {
                    std::cout << ", " << j << ":" << toCheck[j] << ", " << (i+2*(j-i)) << ":" << toCheck[(i+2*(j-i))] << "    found" << std::endl;
                    return 0;
                }
            }
        }
    }
    return -1;
}

int main (int agrc, char* args[]) {
    std::string toCheck("1001011");
    findIt(toCheck);
    std::cin.get();
    return 0;
}

1
技術的には、これはO(n ^ 2)です。平均して、内部ループは実行されるたびにnの半分以上を反復します。したがって、それはO(n *(n / 2))と書くことができ、O(n ^ 2)に簡略化できます
Robert Parker

うーん、あなたは正しいようです。これは単純な問題ではなく、1テイクO(n)をすべて見つけるだけであり、O(logn)複雑度とのさらなる検索/比較の余地はあまりありません。
DaClown 2009年

-3

このアルゴリズムはO(n log n)の複雑さを持っていると思います(C ++、DevStudio 2k5)。現在、アルゴリズムを分析してその複雑さを判断する方法の詳細がわからないため、メトリック収集情報をコードに追加しました。コードは、与えられた入力に対して1と0のシーケンスで行われたテストの数をカウントします(うまくいけば、アルゴリズムのボールを作成していません)。テストの実際の数をO値と比較して、相関関係があるかどうかを確認できます。

#include <iostream>
using namespace std;

bool HasEvenBits (string &sequence, int &num_compares)
{
  bool
    has_even_bits = false;

  num_compares = 0;

  for (unsigned i = 1 ; i <= (sequence.length () - 1) / 2 ; ++i)
  {
    for (unsigned j = 0 ; j < sequence.length () - 2 * i ; ++j)
    {
      ++num_compares;
      if (sequence [j] == '1' && sequence [j + i] == '1' && sequence [j + i * 2] == '1')
      {
        has_even_bits = true;
        // we could 'break' here, but I want to know the worst case scenario so keep going to the end
      }
    }
  }

  return has_even_bits;
}

int main ()
{
  int
    count;

  string
    input = "111";

  for (int i = 3 ; i < 32 ; ++i)
  {
    HasEvenBits (input, count);
    cout << i << ", " << count << endl;
    input += "0";
  }
}

このプログラムは、最大32文字の各文字列長のテスト数を出力します。結果は次のとおりです。

 n  Tests  n log (n)
=====================
 3     1     1.43
 4     2     2.41
 5     4     3.49
 6     6     4.67
 7     9     5.92
 8    12     7.22
 9    16     8.59
10    20    10.00
11    25    11.46
12    30    12.95
13    36    14.48
14    42    16.05
15    49    17.64
16    56    19.27
17    64    20.92
18    72    22.59
19    81    24.30
20    90    26.02
21   100    27.77
22   110    29.53
23   121    31.32
24   132    33.13
25   144    34.95
26   156    36.79
27   169    38.65
28   182    40.52
29   196    42.41
30   210    44.31
31   225    46.23

「n log n」の値も追加しました。選択したグラフ作成ツールを使用してこれらをプロットし、2つの結果の相関関係を確認します。この分析はnのすべての値に拡張されますか?知りません。


それは完全な相関ではありません、私は同意します。ただし、曲線はn ^ 2よりもn log nに近くなります。
2009年

3
入力サイズを100万以上に増やしてみてください。小さな入力では、曲線はアルゴリズムの曲線に似ていることがよくあります。これは、入力のサイズを大きくすると明らかに優れています。
Nick Larsen、

内側のループが外側のループで囲まれた二重のforループは、三角形の形状になりますが、複雑さはO(n ^ 2)のままです。すべての(i、j)について考えます。[0、n]のiと[0、n-2 * i]のj、三角形があり、三角形の面積は2次傾向です。
Matthieu M.

正確には、偶数nに対してTests =(n ^ 2-2n)/ 4です。明らかに二次。
おじいちゃん
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.