web-dev-qa-db-ja.com

以下のクラスで非常に優れた実際のアルゴリズムは存在しますか?

昨夜、私は別のプログラマーと話しました。何かがO(1)の場合でも、O(n)である操作は、O(1)アルゴリズム。彼は同意しなかったので、ここに持ってきました。

その下のクラスのアルゴリズムを大幅に上回るアルゴリズムの例はありますか?たとえば、O(n)はO(1)またはO(n2)O(n)より高速です。

数学的には、定数因子を無視すると、上限が漸近的な関数でこれを示すことができますが、そのようなアルゴリズムは実際に存在しますか?そして、それらの例はどこにありますか?それらはどのような状況で使用されますか?

39
KyleWpppd

非常に小さな固定データテーブルでのルックアップ。最適化されたハッシュテーブルは、O(1)である可能性がありますが、ハッシュ計算のコストのため、バイナリ検索または線形検索よりも低速です。

45
Loren Pechtel

行列の乗算。ナイーブO(n ^ 3)アルゴリズムは、実際には、ストラッセンのO(n ^ 2.8)よりも速く、行列が小さめの行列で使用されます。より大きな行列には、O(n ^ 2.3)Coppersmith–Winogradアルゴリズムの代わりにStrassenが使用されます。

25
Peter Taylor

簡単な例は、さまざまな並べ替えアルゴリズムの違いです。 Mergesort、HeapsortなどはO(n log n)です。クイックソートはO(n ^ 2)最悪の場合です。しかし、多くの場合、Quicksortの方が高速であり、実際には平均してO(n log n)のように実行されます。 詳細

別の例は、単一のフィボナッチ数列の生成です。反復アルゴリズムはO(n)ですが、行列ベースのアルゴリズムはO(log n)です。それでも、最初の数千のフィボナッチ数列については、反復アルゴリズムの方がおそらく高速です。もちろんこれは実装にも依存します!

漸近的なパフォーマンスが優れたアルゴリズムには、パフォーマンスが悪いがオペレーションが単純なアルゴリズムでは不要なコストのかかるオペレーションが含まれる場合があります。最後に、[〜#〜] o [〜#〜]表記は、操作対象の引数が劇的に増加した場合(無限に近づく場合)にのみパフォーマンスについて通知します。

24
molf

注:下記の@ back2dosや他の教祖によるコメントを読んでください。実際に私が書いたものよりも役立つため、すべての貢献者に感謝します。

以下のチャートから考えます(以下から取得: Big O notation 、 "The Pessimistic Nature of Algorithms:")を検索すると、O(log n)がではないことがわかります常に言うより良い、O(n)。だから、私はあなたの議論が正しいと思います。

Pic-1

18
NoChance

nの実用的な値については、はい。これはCS理論でよく出てきます。技術的にはBig-Ohのパフォーマンスが高い複雑なアルゴリズムがよくありますが、定数係数が大きすぎて実用的ではありません。

計算幾何学の教授に、ポリゴンを線形時間で三角測量するアルゴリズムを説明してもらいましたが、「非常に複雑です。実際に実装した人はいないと思います」(!)。

また、フィボナッチヒープは通常のヒープよりも優れた特性を備えていますが、通常のヒープと同じように機能しないため、あまり一般的ではありません 実際には 。これは、ヒープを使用する他のアルゴリズムにカスケードできます。たとえば、ダイクストラの最短パスは、フィボナッチヒープを使用すると数学的に高速ですが、通常は実際には使用されません。

11
Gabe Moothart

リンクリストへの挿入とサイズ変更可能な配列への挿入を比較してください。

リンクされたリストO(1)を挿入する価値があるためには、データの量はかなり大きくなければなりません。

リンクされたリストには、次のポインタと逆参照のための追加のオーバーヘッドがあります。サイズ変更可能な配列は、データをコピーする必要があります。そのコピーはO(n)ですが、実際には非常に高速です。

10
Winston Ewert

Big-Oh表記は関数の成長率を表すために使用されるため、O(1)アルゴリズムの方が高速になる可能性がありますが、特定のポイント(定数係数)。

一般的な表記:

O(1)-反復回数(関数が費やすユーザー時間と呼ばれることもあります)は、入力のサイズに依存せず、実際には一定です。

O(n)-反復数は、入力のサイズに比例してlinear比例して増加します。意味-アルゴリズムが入力N、2 * N回反復する場合でも、O(n)と見なされます。

O(n ^ 2)(二次)-反復回数は、入力サイズの二乗です。

8
Yam Marcovic

Regexライブラリは通常、複雑なO(nm)を持つDFA生成ではなく、最悪の場合の指数関数時間を持つバックトラッキングを実行するために実装されます。

素朴なバックトラッキングは、入力が高速パスに留まっている場合や、過度にバックトラックする必要なく失敗した場合に、パフォーマンスが向上します。

(この決定は単にパフォーマンスに基づくものではありませんが、後方参照を許可するためにも行われます。)

6
dan_waterworth

O(1)アルゴリズム:

_def constant_time_algorithm
  one_million = 1000 * 1000
  sleep(one_million) # seconds
end
_

O(n)アルゴリズム:

_def linear_time_algorithm(n)
  sleep(n) # seconds
end
_

明らかに、nの任意の値に対して_n < one_million_の場合、例で指定されているO(n)アルゴリズムは、O(1)アルゴリズムよりも速いになります。 。

この例は少し面倒ですが、精神的には次の例と同等です。

_def constant_time_algorithm
  do_a_truckload_of_work_that_takes_forever_and_a_day
end

def linear_time_algorithm(n)
  i = 0
  while i < n
    i += 1
    do_a_minute_amount_of_work_that_takes_nanoseconds
  end
end
_

あなたはmustO式の定数と係数を知っており、あなたはmustnの予想される範囲を知っているため、 アプリオリどのアルゴリズムがより高速になるか。

それ以外の場合は、必須予測される範囲のnの値を使用して2つのアルゴリズムをベンチマークして、事後どのアルゴリズムがより高速になったかを判断します。

5
yfeldblum

並べ替え:

挿入ソートはO(n ^ 2)ですが、少数の要素に対して他のO(n * log(n))ソートアルゴリズムよりも優れています。

これが、ほとんどのソート実装が2つのアルゴリズムの組み合わせを使用する理由です。例えば。マージソートを使用して大きな配列を配列の特定のサイズに達するまで分割し、次に挿入ソートを使用して小さな単位をソートし、マージソートで再度マージします。

Timsort を参照してくださいPythonおよびJava 7このソートを使用したソートの現在のデフォルト実装。

4
OliverS

一部の病理学的入力では、実際に使用される nification アルゴリズムは、最悪の場合指数関数的です。

多項式統一アルゴリズム がありますが、実際には遅すぎます。

4
starblue

メモリ内のバブルソートは、プログラムがディスクにスワップされるとき、または比較するときにディスクからすべての項目を読み取る必要があるときに、クイックソートよりもパフォーマンスが優れています。

これは彼が関わっている例です。

3
user1249

多くの場合、より高度なアルゴリズムは、特定の量の(高価な)セットアップを想定しています。一度だけ実行する必要がある場合は、ブルートフォースメソッドを使用したほうがよいでしょう。

例:バイナリ検索とハッシュテーブルルックアップはどちらも、線形検索よりもはるかに高速ですルックアップごとよりも、リストをソートするか、ハッシュテーブルを作成する必要があります。

ソートにはN log(N)のコストがかかり、ハッシュテーブルには少なくともNのコストがかかります。数百または数千のルックアップを実行する場合でも、それはまだ節約された節約です。ただし、1つまたは2つのルックアップのみを実行する必要がある場合は、線形検索を実行して起動コストを節約するだけで十分です。

3
Matthew Scouten

多くの場合、復号化は0(1)です。たとえば、DESのキースペースは2 ^ 56であるため、すべてのメッセージの復号化は一定時間の操作です。そこに2 ^ 56の因数があるだけなので、非常に大きいです。絶え間ない。

1
Zachary K

セットのさまざまな実装が私の頭に浮かびます。最も素朴な方法の1つは、ベクトルを介して実装することです。つまり、removecontainsを意味するため、addもすべてO(N)を使用します。
代替手段は、入力ハッシュを入力値にマッピングするいくつかの汎用ハッシュを介してそれを実装することです。このようなセットの実装は、O(1) for addcontainsおよびremoveに対して実行されます。

Nが約10程度であると仮定すると、最初の実装の方がおそらく高速です。要素を見つけるために必要なのは、10個の値を1個と比較することだけです。
他の実装では、あらゆる種類の巧妙な変換を開始する必要があります。これは、10回の比較を行うよりもはるかにコストがかかる可能性があります。すべてのオーバーヘッドがあるため、キャッシュミスが発生する可能性もありますが、理論的にはソリューションの速度は問題ではありません。

これは、Nが十分に小さい場合、考えられる最悪の実装が適切な実装よりも優れていることを意味しません。これは、Nが十分に小さいことを意味します。つまり、フットプリントとオーバーヘッドが小さい単純な実装では、スケーラビリティを最優先する実装よりも実際に必要な命令とキャッシュミスが少なくなるため、高速になります。

実際のシナリオでどれだけ速いかは、実際にシナリオを作成して測定するまでわかりません。多くの場合、結果は驚くべきものです(少なくとも私にとって)。

1
back2dos

はい、適切に小さいNの場合。Nは常に存在し、その上に常に順序がありますO(1) <O(lg N)<O(N) <O(N log N)<O(N ^ c)<O(c ^ N)(ここでO(1) <O(lg N)は、 O(1)アルゴリズムは、Nが適切に大きく、cが1より大きいいくつかの固定定数である場合)より少ない演算を行います).

特定のO(1)アルゴリズムが正確にf(N) = 10 ^ 100(グーゴル)演算とO(N)アルゴリズムは正確にg(N) = 2 N + 5演算を実行します。O(N)アルゴリズムは、 Nは大体グーゴル(実際にはN>(10 ^ 100-5)/ 2の場合)であるため、Nが1000から10億の範囲であると予想した場合、O(1)アルゴリズム。

または、現実的な比較のために、n桁の数値を掛け合わせるとします。 カラツバアルゴリズム は最大3つのn ^(lg 3)演算(つまり、おおよそO(n ^ 1.585))ですが、 Schönhage–Strassenアルゴリズム はO(N log N log log N) より速い順序 ですが、ウィキペディアを引用すると:

実際、Schönhage–Strassenアルゴリズムは、2 ^ 2 ^ 15〜2 ^ 2 ^ 17(10進数で10,000〜40,000桁)を超える数値に対して、KaratsubaやToom–Cookの乗算などの古い方法よりも優れています。[4] [5] [6 ]

したがって、500桁の数値を掛け合わせる場合、大きなO引数によって「より高速」なアルゴリズムを使用しても意味がありません。

編集:あなたは決定N => f(N)/ g(N)の無限大を取ることにより、決定f(N)比較g(N)を見つけることができます。制限が0の場合、 f(N) <g(N)、制限が無限大の場合f(N)> g(N)、および制限がある場合その他の定数f(N)〜g(N)ビッグO表記に関して。

1
dr jimbob

線形計画法 のシンプレックス法は、最悪の場合指数関数になる可能性がありますが、比較的新しい内点アルゴリズムは多項式になる可能性があります。

ただし、実際には、シンプレックス法の指数関数的な最悪のケースは発生しません。シンプレックス法は高速で信頼性があり、初期の内点アルゴリズムは非常に遅いため、競争力がありませんでした。 (現在、競争力のあるより近代的な内点アルゴリズムがあります競争力があります-しかし、シンプレックス法も...)

1
comingstorm

構築のためのウッコネンのアルゴリズム サフィックス試行 はO(n log n)です。 「オンライン」であるという利点があります。つまり、より多くのテキストを段階的に追加できます。

最近、他のより複雑なアルゴリズムが実際には高速であると主張されています。これは、主にメモリアクセスの局所性が高く、プロセッサキャッシュの使用率が向上し、CPUパイプラインが回避されるためです屋台。たとえば、処理時間の70〜80%がメモリの待機に費やされていると主張する この調査この論文 が「wotd」アルゴリズムについて説明しているのを参照してください。

接尾辞の試行は、遺伝学(遺伝子配列の照合)で重要であり、スクラブルディクショナリーの実装ではそれほど重要ではありません。

0
Ed Staub

常に存在します 明確に定義された問題に対する最速かつ最短のアルゴリズム 。ただし、これは純粋に理論的には(漸近的に)最速のアルゴリズムです。

問題の説明[〜#〜] p [〜#〜]とその問題のインスタンス[〜#〜] i [〜 #〜]、可能なすべてのアルゴリズム[〜#〜] a [〜#〜]および証明Pr、そのようなペアごとにPr[〜#〜] a [〜#〜][〜#〜] p [〜#〜]の漸近的に最速のアルゴリズムです。そのような証明が見つかると、[〜#〜] a [〜#〜][〜#〜] i [〜#に対して実行します〜]

この問題のないペアの検索は複雑ですO(1)(修正された問題の場合[〜#〜] p [〜#〜] )なので、常に問題に対して漸近的に最速のアルゴリズムを使用しますが、この定数はほとんどすべての場合に語り知れないほど巨大であるため、この方法は実際にはまったく役に立ちません。

0
Alex ten Brink

多くの言語/フレームワークでは、文字列の照合に [〜#〜] kmp [〜#〜] の代わりに単純なパターンマッチングを使用しています。アババババババババババババブではなく、トム、ニューヨークのようなひもを探します。

0
Lukasz Madon