以下のクラスで非常に優れた実世界のアルゴリズムが存在しますか?[閉まっている]


39

昨夜、私は別のプログラマーと話し合っていましたが、何かがO(1)であっても、O(1)アルゴリズムに大きな定数がある場合、O(n)の操作はそれを上回る可能性があります。彼は同意しなかったので、ここに持ってきました。

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

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


15
「大きな」アルゴリズムであっても、小さいほど良いとは限りません。たとえば、ガウス消去法はO(n ^ 3)ですが、O(n ^ 2)で実行できるアルゴリズムがありますが、2次時間アルゴリズムの係数は非常に大きいため、人々はO(n ^ 3)1つ。
ブラックジャック

11
これを賢明な質問にするには、「...現実世界の問題」などを追加する必要があります。それ以外の場合nは、定数を補うのに十分な大きさにするだけです(これはbig-O表記のポイントです)。
スターブルー

8
速度のためにビッグO表記を使用しないでください。
コーディズム

16
big-O表記法のポイントは、アルゴリズムの実行速度を示すことではなく、スケーリングの程度を示すことです。
BlueRaja-ダニーPflughoeft

4
LPを解くためのシンプレックスアルゴリズムについて誰も言及していないことに驚いています。実行時間が線形である指数関数的なワーストケースがあります。実際には、非常に高速です。最悪の場合のランタイムも示す問題を作成するのは簡単です。また、頻繁に使用されます。
-ccoakley

回答:


45

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


14
より正確には、ハッシュテーブルルックアップはO(m)です。ここで、mはキーのサイズです。キーサイズが一定の場合にのみ、そのO(1)を呼び出すことができます。また、通常それは償却されます-そうでなければ、テーブルは拡大/縮小できません。ターナリツリーは、文字列が非常に頻繁に見つからないコンテキストでの文字列検索のハッシュテーブルをしばしば破ることができます。ハッシュテーブルバージョンはまだハッシュを計算していません。
Steve314

2
Loren Pechtelの答えとSteve314の最初のコメントが大好きです。私は実際にこれが起こるのを見ました。ハッシュ値を返すのに時間がかかりすぎる(そしてキャッシュしない/できない)hashcode()メソッドを持つJavaクラスを作成する場合、ハッシュタイプコレクションでそのようなクラスのインスタンスを使用します( HashSet)は、そのコレクションを(ArrayListなどの)配列タイプのコレクションよりも遅くします。
シヴァンドラゴン

1
@ Steve314:ハッシュ関数がO(m)で、mがキーのサイズであると仮定するのはなぜですか?ハッシュ関数は、文字列(またはその他の複合型)を扱っている場合でもO(1)になります。入力に間違ったデータ構造(ハッシュテーブル)が選択された場合(キーサイズは予測不能)、単純にハッシュ関数を実現すると複雑さが大幅に変わる可能性があるため、正式な定義に入れるほどの価値はありません。
コーディズム

1
@ Steve314:固定データテーブルについて述べたことに注意してください。彼らは成長しません。また、衝突が発生しないようにキーを最適化できる場合にのみ、ハッシュテーブルからO(1)パフォーマンスを取得します。
ローレンペクテル

1
@Loren-厳密に言えば、テーブルのサイズが固定されている場合、空きスペースの検索に費やすことができる最大時間は一定です。つまり、せいぜいn-1個の既に埋められたスロットをチェックする必要があります。ここで、nは一定のテーブルサイズです。したがって、固定サイズのハッシュテーブルはO(1)であり、償却分析を必要としません。これは、テーブルがいっぱいになるにつれてアクセスが遅くなることを気にしないという意味ではありません-それは大きなOが表現するものではないということだけです。
Steve314

25

行列の乗算。ナイーブO(n ^ 3)アルゴリズムは、実際には、小さな行列の場合、StrassenのO(n ^ 2.8)よりも高速であることがよくあります。O(n ^ 2.3)Coppersmith–Winogradアルゴリズムの代わりにStrassenを使用して、より大きな行列を作成します。



2
Coppersmith-Winogradは決して使用されません。それを実装すること自体が恐ろしい作業であり、定数は非常に悪いため、現代の科学的なマトリックスの問題にとっても実行不可能です。
tskuzzy

24

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

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

漸近的なパフォーマンスが向上したアルゴリズムには、パフォーマンスは低下しますが、操作が単純なアルゴリズムでは必要のない高価な操作が含まれる場合があります。最後に、O表記は、処理対象の引数が劇的に増加する(無限に近づく)場合にのみパフォーマンスに関する情報を提供します。


これはBig-Oの優れた説明ですが、質問の要点に対処することはできません。これは、O(n)アルゴリズムがO(1)よりも高速になる特定のケースです。
KyleWpppd

フィボナッチナンバーワンは少しずれています。出力サイズは入力サイズで指数関数的であるため、O(lg n * e ^ n)とO(lg lg n * e ^ n)の差になります。
ピーターテイラー

補遺:せいぜい。行列ベースのアルゴリズムは、1.5 ^ nのオーダーの数値で乗算を行うため、O(lg lg n * ne ^ n)が証明可能な最良の範囲である可能性があります。
ピーターテイラー

1
クイックソートは通常、とにかくO(n log n)の期待されるパフォーマンスとして記述されます-ランダム入力の場合、最悪のケースはほとんどありません。最悪のケースは、クイックソートが(1)非常に単純で、(2)非常にキャッシュに優しいという事実よりも関連性が低く、どちらも他の多くのソートアルゴリズムよりも大幅に優れた定数係数につながります。
Steve314

(2)big-Oのパフォーマンスを検討する際に考慮する必要があるのは、まさに外部の考慮事項です。アルゴリズム的には、Mergesortは常に Quicksort よりも優れているはずですが、リソースの使用とキャッシュの局所性は一般に、実際のパフォーマンスの位置を逆にします。
ダンライオンズ

18

注:以下の@ back2dosと他の達人によるコメントを読んでください。実際に書いたものよりも役立つので、すべての貢献者に感謝します。

下のチャート(ビッグO表記、「アルゴリズムの悲観的な性質:」を検索)から、O(log n)がO(n)よりも常に優れているとは限らないことがわかります。だから、あなたの議論は有効だと思う。

写真-1


6
質問は、アルゴリズムの特定の実世界の例を求めていました。これは現状のままではありません。
ミーガンウォーカー

19
そのグラフには何も表示されないため、質問に答えることができます。誤解を招く。このグラフは、単に機能をプロットy = 1y = log xなどとの交点y = 1y = x実際のポイントです(1,1)。あなたが言うよりも、これが本当に正しい場合、より複雑なアルゴリズムは、0から2エントリに対してより速くなる可能性があり、これは人々がほとんど気にしないものです。グラフが完全に考慮に入れていないもの(および問題の知覚可能なパフォーマンスの違いは何から来るのか)は一定の要因です。
back2dos

@Samuel Walker、コメントありがとう。提供されるリンク(Link-1)には、カテゴリごとのアルゴリズムの例がいくつかあります。
-NoChance

5
@ back2dos:グラフ自体は質問に答えませんが、答えに使用できます。表示される各関数の形状は、どのスケールおよび定数ファクターでも同じです。これにより、グラフは、与えられた関数の組み合わせで、一方が小さい入力範囲と、もう一方が小さい入力範囲があることを示しています。
ジャン・ヒューデック

2
@dan_waterworth、あなたは正しい、私はその点を認め、そのコメントを削除します。それにもかかわらず、答えは2つの点で間違っているか誤解を招きます。1)Big-Oの全体的なポイントは、複雑さの上限を与えることです。nが大きくなると最大の用語に圧倒される小さな用語を明示的に破棄するため、大きなnに対してのみ意味があります。2)質問のポイントは、Big-Oの上限が高いアルゴリズムが下限のアルゴリズムを上回る2つのアルゴリズムの例を見つけることです。この答えは、そのような例を示していないため失敗します。
カレブ

11

の実用的な値についてはn、はい。これは、CS理論でよく取り上げられます。多くの場合、技術的にbig-Ohのパフォーマンスが優れた複雑なアルゴリズムがありますが、定数係数は非常に大きいため、実用的ではありません。

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

また、フィボナッチヒープは、通常のヒープよりも優れた特性を持っている、しかし、彼らは同様に行いませんので、非常に人気がありません実際には、通常のヒープとして。これは、ヒープを使用する他のアルゴリズムにカスケードできます。たとえば、ダイクストラの最短パスはフィボナッチヒープを使用すると数学的に高速になりますが、実際にはそうではありません。


100,000程度の頂点の巨大なグラフの場合は高速です。
tskuzzy

フィボナッチヒープも最初の(実際には、2番目の)考えでした。
コンラッドルドルフ

10

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

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

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


1
サイズ変更可能な配列は、いっぱいになるたびにサイズが2倍になるため、挿入ごとのサイズ変更の平均コストはO(1)です。
ケビンクライン

2
@kevincline、はい。ただし、O(n)は、挿入ポイントの後のすべての要素を前方に移動する必要があるためです。割り当てはO(1)時間で償却されます。私のポイントは、その動きはまだ非常に速いので、実際には通常、リンクされたリストを破ることです。
ウィンストンイーバート

連続した配列がリンクリストと比較して非常に高速である理由は、プロセッサのキャッシュによるものです。リンクリストをトラバースすると、すべての要素のキャッシュミスが発生します。両方の世界を最大限に活用するには、展開されたリンクリストを使用する必要があります。
dan_waterworth

サイズ変更可能な配列は常にコピーするとは限りません。何を実行しているのか、そして何か障害があるかどうかに依存します。倍増サイズ、実装固有の同じです。ただし、ロールオーバーロールオーバーは問題です。リンクリストは通常​​、サイズが不明なキューに最適です。ただし、ロータリーバッファーはキューにコストをかけます。他のケースでは、リンクリストは便利です。なぜなら、割り当てや展開では常に連続したものを使用できるわけではないので、とにかくポインターが必要になるからです。
jgmjgm

@jgmjgm、サイズ変更可能な配列の中央に挿入すると、その後の要素は絶対にコピーされます。
ウィンストンイーバート

8

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

一般的な表記法:

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

O(N) -反復数はで成長線形入力の大きさに比例。意味-アルゴリズムが任意の入力N、2 * N回を反復する場合、O(n)と見なされます。

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


2
O(1)メソッドは呼び出しごとに37年かかるのに対し、O(n)メソッドは呼び出しごとに16 * nマイクロ秒かかる場合があります。どちらが速いですか?
カズドラゴン

16
私はこれがどのように質問に答えるかを完全に見当たりません。
-avakar

7
私はビッグオーを理解しています。これは、実際の質問には対応していません。これは、big-Oの低いアルゴリズムがbig-Oの高いアルゴリズムよりも優れている関数の具体例です。
-KyleWpppd

「例はありますか...」という形式で質問をすると、必然的に誰かが「はい」と答えます。何も与えずに
-rakslice

1
@rakslice:たぶんそう。ただし、このサイトでは、あなたが行った声明の説明(またはより良いが、証拠)が必要です。今、そのような例があることを証明するための最良の方法は、1つを与えることです;)
back2dos

6

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

素朴なバックトラッキングは、入力が高速パス上にとどまるか、過度にバックトラックする必要なく失敗する場合に、より優れたパフォーマンスを発揮できます。

(この決定はパフォーマンスに基づいているだけではありませんが、後方参照も許可しています。)


それは部分的に歴史的なものだと思います-正規表現をDFAに変換するアルゴリズムは、初期のツール(sedとgrep、私が推測する)のいくつかが開発されたときに特許を取得しました。もちろん、私はコンパイラーの教授からこれを聞いたが、彼は完全には確信が持てなかったので、これは第三者の説明だ。
ティコンジェルビス

5

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

明らかに、任意の値に対して、実施例で与えられたアルゴリズムは、あろう速くよりアルゴリズム。nn < one_millionO(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

式の定数と係数を知っておく必要があり、どのアルゴリズムがより高速になるかを事前に決定するために、の予想される範囲を知る必要Oあります。n

そうしないと、あなたはしなければならないベンチマークの値を持つ2つのアルゴリズムnの順序で予想の範囲内で決定するために事後速いことになったアルゴリズム。


4

並べ替え:

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

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

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



3

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

これは彼が関係できる例のはずです。


クイックソートとバブルソートで引用されている複雑さは、O(1)ランダムメモリアクセスを想定していませんか?これが当てはまらない場合、クイックソートの複雑さを再検討する必要はないでしょうか?
ヴィクトルダール

@ViktorDahl、アイテムのアクセス時間は、ソートアルゴリズムの複雑さで従来測定されているものの一部ではないため、ここでは「O(1)」は正しい単語の選択ではありません。代わりに「一定時間」を使用してください。-ソートアルゴリズムに関するバックはいくつかの項目は、他の(仮想メモリ)よりも盗んがより高価であることを知りながらPHKは、物品Aを書いたqueue.acm.org/detail.cfm?id=1814327 -あなたはそれが面白いかもしれません。

私は今間違いを見ました。通常、比較の数を測定しますが、もちろん記憶媒体の速度の影響を受けません。また、リンクをありがとう。
ヴィクトールダール

3

多くの場合、より高度なアルゴリズムは、ある程度の(高価な)セットアップを前提としています。一度だけ実行する必要がある場合は、ブルートフォースメソッドを使用することをお勧めします。

たとえば、バイナリ検索とハッシュテーブルルックアップはどちらもリニアサーチよりもルックアップごとにはるかに高速ですが、それぞれリストをソートするかハッシュテーブルを作成する必要があります。

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


1

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


メッセージの復号化はO(n)ではありません。nはメッセージのサイズに比例しますか?正しいキーを持っている限り、キーのサイズは考慮されません。いくつかのアルゴリズムには、キーのセットアップ/拡張プロセスが最小限かまったくありません(DES、RSA-キーの生成は依然として複雑なタスクですが、キーの拡張とは関係ありません)が、他のアルゴリズムは非常に複雑です(Blowfishが思い浮かびます)完了すると、実際の作業にかかる時間はメッセージのサイズに比例するため、O(n)になります。
CVn

あなたはおそらく解読ではなく暗号解読を意味しますか?

3
ええ、はい、あなたは定数であり、アルゴリズムをO(1)であると宣言するために取ることができるものがいくつもあります。[並べ替えは、要素が比較するのに一定の時間がかかることを暗黙的に想定しています。たとえば、任意の数学と非bignumの数値]
-Random832

1

セットのさまざまな実装が思い浮かびます。最もナイーブの一つは、手段、ベクトルの上に実装されているremoveだけでなく、containsしたがって、またadd、すべてのテイクO(N)。
別の方法は、入力ハッシュを入力値にマッピングする汎用ハッシュを介して実装することです。このようなセット実装はaddcontainsおよびのO(1)で実行されますremove

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

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

現実世界のシナリオで何かをどれだけ速くするかは、実際にそれを入れて単純に測定するまでわかりません。多くの場合、結果は驚くべきものです(少なくとも私には)。


1

はい、適切に小さいNの場合。常にNがあり、それを超えると常に順序O(1)<O(lg N)<O(N)<O(N log N)<O(N ^ c )<O(c ^ N)(ここでO(1)<O(lg N)は、Nが適切に大きく、cが1より大きい固定定数である場合、O(1)アルゴリズムで実行される操作が少なくなることを意味します)。

特定のO(1)アルゴリズムが正確にf(N)= 10 ^ 100(googol)操作を行い、O(N)アルゴリズムが正確にg(N)= 2 N + 5操作を行うとします。O(N)アルゴリズムは、Nがほぼグーグルになるまで(実際にはN>(10 ^ 100-5)/ 2になるまで)パフォーマンスが向上します。 O(1)アルゴリズムを使用すると、大きなペナルティが発生します。

または、現実的な比較のために、n桁の数字を乗算するとします。カラツバアルゴリズムはながら(約O(N ^ 1.585である))は、最も3 N ^(LG 3)操作であるSchönhage-StrassenのアルゴリズムがあるO(NログNログログN)で高速順が、引用しますウィキペディア:

実際には、シェーンハーゲストラッセンアルゴリズムは、2 ^ 2 ^ 15から2 ^ 2 ^ 17(10,000から40,000桁)を超える数のKaratsubaやToom-Cook乗算などの古い方法よりも優れています。[4] [5] [6 ]

したがって、500桁の数字を一緒に乗算する場合、大きなO引数により「高速」なアルゴリズムを使用することは意味がありません。

編集:f(N)/ g(N)の制限N->無限大を取ることにより、g(N)と比較してf(N)を決定できます。制限が0の場合はf(N)<g(N)、制限が無限の場合はf(N)> g(N)、制限が他の定数の場合はf(N)〜g(N)大きなO表記の観点から。


1

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

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


0

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

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

接尾辞の試行は、遺伝学(遺伝子配列のマッチング)で重要であり、多少重要ではありませんが、スクラブル辞書の実装でも重要です。


0

明確に定義された問題には、常に最速で最短のアルゴリズムがあります。ただし、純粋に理論的には(漸近的に)最速のアルゴリズムにすぎません。

問題のいずれかの記述を考えるとPとその問題のインスタンスI、それはすべての可能なアルゴリズムを列挙AとプルーフのPrをするかどうか、そのような各ペアをチェックし、Prは、有効な証拠であるAは、のために漸近的に最速のアルゴリズムであるP。そのような証明を見つけると、Iに対してAを実行します。

この問題のないペアの検索には、複雑なO(1)(固定された問題Pの場合)があるため、常に問題に対して漸近的に速いアルゴリズムを使用します。ただし、この定数はほとんどすべての場合で非常に大きいため、この方法は実際にはまったく役に立ちません。


0

多くの言語/フレームワークでは、KMPの代わりに単純なパターンマッチングを使用して文字列を照合します。ababaabababababaabababababababではなく、Tom、New Yorkのようなストリングを探します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.