web-dev-qa-db-ja.com

O(nlogn)アルゴリズム-バイナリ文字列内の3つの等間隔のものを検索

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

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

そのため、これらの文字列には「等間隔」の3つの文字列があります:11100000、0100100100

編集:これはランダムな数字なので、どんな数字でも動作するはずです。私が与えた例は、「等間隔」プロパティを説明することでした。したがって、1001011は有効な数値です。 1、4、および7は等間隔に配置されたものです。

173
Robert Parker

最後に! sdcvvc's answer でリードを追跡すると、問題に対するO(n log n)アルゴリズムがあります!理解した後も簡単です。 FFTを推測した人は正しかった。

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

  1. Sを左から右にスキャンし、1の位置のリストLを作成します。上記のS=110110010には、リストL = [1、2、4、5、8]があります。 。このステップはO(n)です。問題は、長さの算術的進行を見つけることです3 in L、すなわち、別個のa、b 、c in L in ba = cb、または同等のa + c = 2b上記の例では、進行(2、5、8)を見つけます。

  2. polynomialpを項xkk in L。上記の例では、多項式p(x) =(x + x2 + x4 + x5+ x8。このステップはO(n)です。

  3. 多項式を見つけるq = p2高速フーリエ変換 を使用します。上記の例では、多項式q(x) = x16 + 2x13 + 2x12 + 3x10 + 4x9 + x8 + 2x7 + 4x6 + 2x5 + x4 + 2x3 + x2このステップはO(n log n)です。

  4. xに対応する用語を除くすべての用語を無視します2kいくつかのk in L。上記の例では、用語x16、3x10、 バツ8、 バツ4、 バツ2。このステップは、実行することを選択した場合、O(n)です。

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

そのため、アルゴリズムはこれらの項の係数x2b、およびそれらのいずれかが1より大きいかどうかを確認します。ない場合は、等間隔の1はありません。 is a b in in Lの係数がx2bが1より大きい場合、(a、c)(b、b)以外にa + c = 2b。実際のペアを見つけるには、それぞれaL(対応するc2b-aになります)を試して、 Sの位置2b-aの1このステップはO(n)です。

以上です。


FFTを使用する必要がありますか? beta'sflybywire's 、および rsp's などの多くの回答は、1の各ペアをチェックして1があるかどうかを確認するアプローチを示唆しています。 「3番目」の位置で、O(n log n)で機能する可能性があります。1が多すぎるとトリプルが簡単に見つかり、1が少なすぎると、すべてのペアのチェックに時間がかかりません。 。残念ながら、この直感は正しいものの、O(nよりも簡単なアプローチis2)、それはそれほど良くありません。 sdcvvc's answer のように、長さn = 3の文字列の「キャンターのようなセット」を取ることができますk、3進表現に0と2のみ(1なし)が含まれる位置に1を付けます。このような文字列には2k = n(ログ2)/(ログ3) ≈n0.63個で、等間隔の1はありません。したがって、すべてのペアのチェックは、その中の1の数の2乗のオーダーになります。つまり、4k ≈n1.26残念ながら漸近的に(n log n)よりもはるかに大きい。実際、最悪の場合はさらに悪い:1953年のLeo Moser 構築済み (事実上)n1-c /√(log n) 1s、ただし等間隔の1sはありません。つまり、このような文字列では、単純なアプローチはΘ(n2-2c /√(log n)tinyビットのみがΘ(n2、驚くほど!


長さnのストリングに含まれる1の最大数について、等間隔の3つはありません(上記で見たように、少なくともn0.63簡単なCantorのような構造から、少なくともn1-c /√(log n)モーザーの構成)—これは OEIS A003002 です。また、A065825(k)≤n <A065825(k + 1)となるように、kとして OEIS A065825 から直接計算することもできます。これらを見つけるためのプログラムを作成しましたが、貪欲なアルゴリズムはnotがそのような最長の文字列を与えることがわかりました。たとえば、n = 9の場合、5個の1(110100011)を取得できますが、貪欲は4(110110000)のみを提供し、n = 26の場合、11個の1s(11001010001000010110001101)しかし、貪欲は8(11011000011011000000000000)のみを提供し、n = 74の場合、22 1(11000010110001000001011010001000000000000000010001011010000010001101000011)を取得できますが、貪欲は16(11011000011011000000000000011011000011011000000000000000000000000000000000)のみを提供します。ただし、50人まではかなりの数の場所で同意します(たとえば、38から50のすべて)。 OEISの参考文献が述べているように、Jaroslaw Wroblewskiはこの質問に興味があるようで、彼はこれらのWebサイトを管理しています 非平均セット 。正確な数は194までしかわかりません。

127
ShreevatsaR

あなたの問題は このペーパー (1999)でAVERAGEと呼ばれています:

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

ウィキペディア

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

これで問題を解決できます:)。

重要なのはvery重要なのは、O(n log n)が1の数ではなく0と1の数の点で複雑であることです(1の数ではありません) [1,5,9,15]のような配列として与えられます)。セットに1の数の項である算術級数があるかどうかを確認することは難しく、1999年のその論文によれば、O(n2)既知であり、存在しないと推測されます。 これを考慮に入れていない人は誰でも、未解決の問題を解決しようとしています。

その他の興味深い情報、ほとんどが重要ではありません:

下限:

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

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

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

ところで、論文は私が最初に言及したのと同じ年である1999年からのものであるため、おそらく最初の論文ではその結果について言及していないのでしょう。

35
sdcvvc

これは解決策ではありませんが、 Olexiyが考えていたもの に類似した考え方です。

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

  • 11011000011011000000000000001101100001101100000000000000000000000000000000000000000000000110110000110110000000000000011011000011011
  • 1011010001011010000000000001011010001011010000000000000000000000000000000000000000000001101101000101101000000000000101101000101101
  • 10011001010011001000000000010011001010011001000000000000000000000000000000000000010011001010011001000000000010011001010011001

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

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

更新:残念ながら、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
8
z -

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

私はいくつかの手を振る引数を考え出しましたが、きちんとした証拠を見つけることができませんでした。暗闇の中で突き刺すつもりです。答えは非常に賢明なアイデアであり、教授は長い間知っていたので、それは明白に見えるようになりましたが、学生にとってはあまりにも難しいです。 (それまたはあなたはそれを扱った講義を眠った。)

6
Beta

改訂: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. 偶数および奇数プールの使用
    この記述の最後の観測に基づいて、コードは偶数のペアと奇数のペアを検索して、Mを固定したままLとUを見つけます。これは、情報を事前に計算することで検索の回数を減らします。したがって、コードは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

等々。これにより、パフォーマンスがさらに最適化されますが、アルゴリズムの速度は向上しません。

3
hughdbrown

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

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

2
Olexiy

これが役立つ場合があります。

この問題は次のようになります。

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

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

削減は次のとおりです。

ゼロと1のシーケンスが与えられ、左端から開始して、右に移動し続けます。別の1つが検出されるたびに、前の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)で計算できます。

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

2
yfeldblum

私はうまくいくかもしれない分割統治アプローチを考えました。

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

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

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

次に、それを半分に分割します。

100000101 01000100

サイズ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:Return(0、{4,5,6,7})0101:Return(5、{2,3,4,5,6,7})、(7、{1,2,3,4,5,6,7})0100:Return(9、{3,4,5,6,7})0100:リターン(13、{3,4,5,6,7})

8つのセットに結合:

10000101:Return(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)をチェックします-または、2で停止できます。実際に一致します。

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

100%かどうかわからない O(n log n) 最後のステップのため、しかしそこまではすべて間違いなく O(n log n) 私の知る限り。後でこれに戻り、最後のステップを改良してみます。

編集:答えを変更して、ウェルボッグのコメントを反映させました。エラーが発生してすみません。後で書いたものを解読するためにもう少し時間ができたら、後でいくつかの擬似コードも書きます。 ;-)

1
Platinum Azure

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

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. 中間の文字が1である場合よりも、そうでない場合は成功します。それ以外の場合は、midle要素なしで、midleの文字列を分割し、両方の部分を確認します。

複雑さについては、これはO(nlogn)になる可能性があります。各再帰では2で割っています。

それが役に立てば幸い。

1
Beku

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

  • 「1」を探す
  • 次の位置スキャンから開始して別の「1」(配列の最後から現在の最初の「1」までの距離を引くか、3番目の「1」が範囲外になる)
  • 2番目の '1'に最初の1 'までの距離を加えた位置に3番目の' 1 'が見つかった場合、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;
 }
}
1
rsp

単純な問題タイプの場合(つまり、3つの "1" (ゼロ以上) "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つだけを探していますが、私は現在、それを見つける方法がわかりません...

1
MartinStettner

OKバランスの取れた二分木を使用して1間の距離を保存することで、すでに説明したアルゴリズムに似たO(n log(n))アルゴリズムを証明できると思います。このアプローチは、1間の距離のリストに問題を減らすことについてのJusticeの観察に触発されました。

入力文字列をスキャンして、各ノードが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))の時間で実行できると思います。

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

1
Jeremy Bourque

ここで大まかな推測を行い、複雑さの計算が上手な人に、私のアルゴリズムがO表記法でどのように運ばれるかについて助けてもらいます

  1. 与えられたバイナリ文字列0000010101000100(例として)
  2. ゼロのクロップヘッドとテール-> 00000 101010001 00
  3. 前の計算から101010001を取得します
  4. 真ん中のビットが「1」であるかどうかをチェックします。真であれば、有効な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;
}
1
andycjw

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

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

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
Jason S

この問題に対する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のべき乗を打った後、かなり一貫したジャンプがあります。

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
CoderTao

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

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

-

ルートから開始します。その下の1の総数の2乗が許容コストよりも小さい場合、単純なアルゴリズムを適用します。それ以外の場合は、その子に対して再帰します。

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

今では物事は非常に乱雑になります。基本的に、範囲を制限しながら、潜在的な子のセットを再帰的に処理したいと考えています。ナイーブアルゴリズムが予算内で実行されるように範囲が十分に制限されたら、すぐに実行します。私はそれが退屈であることを保証するため、これを実装してお楽しみください。十数件のケースがあります。

-

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

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

0
Craig Gidney
_# <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万を超える長さの文字列を生成できますか?

0
Bob Aman

問題を解決する方法を見つけたと思いますが、正式な証拠を作成することはできません。私が作成したソリューションは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)なので、とにかくアルゴリズムの複雑さに影響を与えません。

0
Robert Parker

私の最善の努力にもかかわらず、弓で自分自身を包み込むようには思われないいくつかの考えがあります。それでも、彼らは誰かの分析のための有用な出発点かもしれません。

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

  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までであるため、全体では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";
0
Jeremy Bourque

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

編集: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
0
Niek H.

私は数学的アプローチを提示しようとします。これは終わりではなく始まりですので、助け、コメント、矛盾さえあれば大歓迎です。ただし、このアプローチが実証されている場合、アルゴリズムは文字列内の単純な検索です。

  1. 固定数のスペースkと文字列Sが与えられた場合、kスペーストリプレットの検索にはO(n)がかかります-S[i]==S[i+k]==S[i+2k]の場合、すべての0<=i<=(n-2k)をテストします。このテストではO(1)を使用し、kが定数であるn-k回実行するため、O(n-k)=O(n)を使用します。

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

  3. 私が探す必要があるスペースの可能な数の上限kがあるとします。ここで、1にある各S[i]について、1およびS[i-1]S[i+1]およびS[i-2]、...S[i+2]およびS[i-k]S[i+k]を確認する必要があります。これには、S内の各1に対してO((k^2-k)/2)=O(k^2)が必要です。これは、 Gauss 'Series Summation Formula が原因です。これはセクション1とは異なることに注意してください-定数スペースとしてではなく、スペース数の上限としてkを持っています。

O(n*log(n))を証明する必要があります。つまり、k*(number of 1's)log(n)に比例することを示す必要があります。

それができれば、アルゴリズムは簡単です。インデックスがSであるiの各1について、距離kまでの各側から1を探すだけです。同じ距離に2つ見つかった場合は、ikを返します。繰り返しになりますが、トリッキーな部分はkを見つけて正しいことを証明することです。

私はここであなたのコメントを本当に感謝します-私はkとホワイトボード上の1の数の間の関係を見つけようとしましたが、これまでのところ成功していません。

0
Adam Matan

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

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

ステージ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
Rob Rolnick

これは楽しい問題が好きだったので、私はそれを試してみることにしました。

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

これは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

これは最初の試みであるため、これはよりクリーンな方法で記述できると確信しています。この方法が失敗する場合を以下にリストしてください。

0
James McMahon

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

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

仮定:

間違っている、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)のように見えます。

この式を取得した方法は?

文字列にカントール番号を配置します。
文字列の長さが3 ^ p == nであるとします
Cantor文字列の生成の各ステップで、前の数の2/3を保持します。これをp回適用します。

つまり、(n *((2/3)^ p))->(((3 ^ p))*((2/3)^ p))残りのもの、および簡略化後の2 ^ p。これは、3 ^ p string->(3/2)^ p onesの2 ^ pを意味します。 p = log(n)/ log(3)を代入して取得
((3/2)^(log(n)/ log(3)))

0
Luka Rahne

1をスキャンしながら、その位置をリストに追加します。 2番目以降の1を追加する場合、これまでのリストの各位置と比較します。間隔はcurrentOne(中央)-previousOne(左)と等しくなります。右側のビットはcurrentOne +スペースです。 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
steamer25

問題への1つの侵入は、要因とシフトを考えることです。

シフトでは、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))であることだと思います。

[〜#〜] wrong [〜#〜]

同僚が指摘したように、ステップ1は間違っています。位置2,12および102に1がある場合、10のモジュラスを取ると、それらはすべて同じ剰余を持ちますが、等間隔ではありません!ごめんなさい。

0