低い方のアルゴリズムよりも高いBig-O時間の複雑性アルゴリズムを好むケースはありますか?


242

O(log n)時間の複雑さよりも時間の複雑さを好むケースはありますO(1)か?それともO(n)O(log n)

例はありますか?


67
前者は理解できるが、後者は理解できないのであれば、O(log n)アルゴリズムよりもアルゴリズムを好むO(1)...
Codor

14
理論的なコンピューターサイエンスのO(1)演算を使用した非実用的なデータ構造が多数あります。1つの例は、ビットベクトルのselect()です。これは、5層の間接参照を使用して、操作ごとにo(n)の追加スペースとO(1)でサポートできます。Oと組み合わせた単純な二分探索は、(1)のランク()は、の著者によれば、実際に高速であることが判明簡潔データ構造ライブラリ
ニクラスB.

17
漸近的な複雑性が低くなっても、ランタイムが速くなることは保証されません。具体例として行列乗算を研究します。
Connor Clark、

54
また、十分に大きなテーブルルックアップが与えられれば、どのアルゴリズムもO(1)に変換できます;)
Connor Clark

19
@Hoten-それは、テーブルルックアップがO(1)であることを前提としています。これは、あなたが話しているテーブルのサイズについてはまったく与えられていません。:)
Jander

回答:


267

大きなO時間の複雑性が低いアルゴリズムよりも高いアルゴリズムを好む理由は数多くあります。

  • ほとんどの場合、Big-Oの複雑さを低くすることは困難であり、熟練した実装、多くの知識と多くのテストが必要です。
  • big-Oは定数に関する詳細を隠します。実行されるアルゴリズム10^51/10^5 * log(n)O(1)vs O(log(n))よりもbig-Oの観点からは優れていますが、最も妥当な場合nは、最初のアルゴリズムの方が優れています。たとえば、行列の乗算は最も複雑ですO(n^2.373)が、定数が非常に高いため、(私の知る限り)計算ライブラリでは使用されません。
  • big-Oは、大きなものを計算するときに意味があります。3つの数値の配列をソートする必要がある場合、使用するO(n*log(n))O(n^2)アルゴリズムを使用するかはほとんど問題になりません。
  • 時々、小文字の時間の複雑さの利点は本当に無視できます。たとえば、データ構造のタンゴツリーがあり、O(log log N)、アイテムを見つけるのに時間の複雑さをが、同じものを見つけるバイナリツリーもありますO(log n)。非常に多くのn = 10^20違いがあったとしてもごくわずかです。
  • 時間の複雑さはすべてではありません。で実行されO(n^2)O(n^2)メモリを必要とするアルゴリズムを想像してみてください。それはより好ましいかもしれませんO(n^3)O(1)nが実際には大きくない場合は、時間と空間の場合があります。問題は、長い間待つことができるということですが、アルゴリズムで使用するのに十分な大きさのRAMを見つけることができることは間違いありません
  • 並列化は、私たちの分散世界では優れた機能です。簡単に並列化できるアルゴリズムもあれば、まったく並列化できないアルゴリズムもあります。複雑さの少し高い1台のマシンを使用するよりも、複雑さの高い1000台のコモディティマシンでアルゴリズムを実行することが理にかなっている場合があります。
  • 一部の場所(セキュリティ)では、複雑さが要件になる場合があります。非常に高速にハッシュできるハッシュアルゴリズムを必要とする人はいません(そのため、他の人がより強力にブルートフォースを実行できるためです)。
  • これは複雑さの切り替えとは関係ありませんが、一部のセキュリティ機能はタイミング攻撃防ぐ方法で作成する必要があります。彼らはほとんど同じ複雑さのクラスにとどまりますが、何かを行うには常に最悪のケースをとるように変更されます。1つの例は、文字列が等しいことを比較することです。ほとんどのアプリケーションでは、最初のバイトが異なる場合は高速でブレークするのが理にかなっていますが、セキュリティ上は、最後に悪いニュースを伝えるのを待つことになります。
  • 誰かがより複雑度の低いアルゴリズムの特許を取得しており、企業はお金を払うよりも複雑度を高くする方が経済的です。
  • 一部のアルゴリズムは特定の状況によく適合します。たとえば、挿入ソートの平均時間の複雑さはO(n^2)、クイックソートやマージソートよりも劣りますが、オンラインアルゴリズムとして、値のリストを(ユーザー入力として)受け取ったときに効率的にソートできます。値の完全なリスト。

6
また、中央のアルゴリズムのビッグOに集中しているが、セットアップコストは無視していることを何度か目にしました。たとえば、ハッシュテーブルを作成することは、何度も繰り返す必要がない場合、配列を線形的に処理するよりもコストがかかる可能性があります。実際、最近のCPUの構築方法により、バイナリ検索のようなものでも、線形検索と同じくらい高速にソート済み配列を処理できます。プロファイリングは必要です。
Luaan

@Luaan「実際、最近のCPUの構築方法により、バイナリ検索のようなものでも、線形検索と同じくらい高速にソートされた配列を処理できます。プロファイリングが必要です。」面白い!現代のCPUでバイナリ検索と線形検索に同じ時間がかかる方法を説明できますか?
DJG

3
@Luaan-気にしないで、私はこれを見つけました:schani.wordpress.com/2010/04/30/linear-vs-binary-search
DJG

2
@DenisdeBernardy:いいえ、実際にはありません。それらはPのアルゴリズムである可能性があります。また、並列化することの意味が合理的に定義されていなくても、P!= NPを意味するものではありません。また、非決定論的チューリングマシンの実行可能な実行の検索は非常に並列化できることも覚えておいてください。
einpoklum

228

隠された定数は常に存在し、O(log n)アルゴリズムではより低くなる可能性があります。そのため、実際のデータの場合、実際にはより速く機能します。

スペースの問題もあります(トースターでの実行など)。

また、開発者の時間に関する懸念もあります。O(log n)は、実装と検証が1000倍も簡単です。


いいね、ありがとう。プログラムの安定性を確保するためにO(logn)アルゴリズムを検討することも価値があると考えていました(例:自己平衡バイナリツリー)
V.Leymarie

16
考えられる1つの例:ソートされた小さな配列の場合、完全なハッシュマップの実装を記述して代わりに使用するよりも、プログラマーがバイナリ検索関数を実装する方が簡単でコンパクトです。
大佐32

5
複雑さの例:ソートされていないリストの中央値を見つけることは、O(n * log n)では簡単ですが、O(n)では困難です。
Paul Draper

1
-1、トースターにログを入れないでください...冗談はさておき、これはその場です。ほとんどの操作は違いに気付かないほど大きいlg nので、非常に近いです。kn
corsiKa

3
また、ほとんどの人がよく知っているアルゴリズムの複雑さは、キャッシュ効果を考慮に入れていないという事実もあります。ほとんどの人によると、バイナリツリーで何かを検索することはO(log2(n))ですが、実際にはバイナリツリーのローカリティが悪いため、はるかに悪くなります。
Doval

57

メモリにバインドされたアプリケーションについてまだ誰も言及していないことに驚いています。

アルゴリズムが複雑であるため(つまり、O(1)< O(log n))、または複雑さの前の定数が小さいため(つまり、2 n 2 <6 n 2)、浮動小数点演算が少ないアルゴリズムがある可能性があります。。いずれにせよ、低いFLOPアルゴリズムの方がメモリにバインドされている場合は、より多くのFLOPを使用するアルゴリズムを好むかもしれません。

「メモリバウンド」とは、常にキャッシュの外にあるデータにアクセスすることが多いということです。このデータをフェッチするには、実際にメモリ空間からキャッシュにメモリをプルしてから、データに対して操作を実行する必要があります。このフェッチ手順は、多くの場合非常に遅く、操作自体よりもはるかに遅くなります。

したがって、アルゴリズムがより多くの操作を必要とする場合(ただし、これらの操作はすでにキャッシュにある[したがってフェッチする必要はありません]のデータに対して実行されます)、アルゴリズムは実行する操作が少ないため、アルゴリズムのパフォーマンスは低下します(これは、 -実際のウォールタイムの観点からデータをキャッシュします(したがって、フェッチが必要です))。


1
Alistraは、「スペースの問題」について話しているときに間接的にこれに対処しました
Zach Saucier

2
大量のキャッシュミスは、最終的な実行に定数値(1.6 GHzのRAMを搭載した4コアの3.2 GHz CPUの場合は8以下である)を乗算するだけなので、大きい値では固定定数としてカウントされます。 -O表記。したがって、キャッシュミスの原因は、nのしきい値を移動することだけです。この場合、そのO(n)解はO(1)解よりも遅くなります。
Marian Spanik

1
@MarianSpanikもちろんあなたは正しいです。しかし、この質問は、私たちが好むような状況を尋ねO(logn)オーバーO(1)。実行可能なすべての状況で、nメモリが少ないアプリケーションが、より高い複雑度でさえ、より速いウォールタイムで実行される状況を非常に簡単に想像できます。
NoseKnowsAll 2015

@MarianSpanikは最大300クロックサイクルのキャッシュミスではありませんか?8はどこから来るのですか?
2017

43

データのセキュリティが懸念される状況では、より複雑なアルゴリズムの方がタイミング攻撃への耐性が優れている場合、より複雑なアルゴリズムの方が、それほど複雑ではないアルゴリズムよりも望ましい場合があります。


6
あなたが言ったことは真実ですが、その場合、O(1)で実行されるアルゴリズムは、定義上、タイミング攻撃の影響を受けません。
Justin Lessard

17
@JustinLessard:O(1)であることは、アルゴリズムのランタイムが定数によって制限された後、いくつかの入力サイズがあることを意味します。このしきい値より下で何が起こるかは不明です。また、アルゴリズムの実際の使用では、しきい値を満たさない場合もあります。アルゴリズムは線形であるため、たとえば、入力の長さに関する情報がリークする可能性があります。
イェルクWミッターク

12
ランタイムは制限されたまま、さまざまな方法で変動する可能性もあります。ランタイムがに比例している場合(n mod 5) + 1、それはまだですがO(1)、に関する情報を明らかにしますn。したがって、漸近的に(そして場合によっては実際にさえ)遅くなる場合でも、よりスムーズなランタイムを備えたより複雑なアルゴリズムが望ましい場合があります。
Christian Semrau

これが、基本的にbcryptが優れていると見なされる理由です。それは物事を遅くします
デビッドは

@DavidGrinbergそれがbcryptが使用される理由であり、質問に適合します。しかし、これはタイミング攻撃について語っているこの回答とは無関係です。
Christian Semrau

37

Alistraはそれを釘付けにしたが、例を提供できなかったので、私はそうします。

あなたはあなたの店が売るもののために10,000のUPCコードのリストを持っています。10桁のUPC、価格の整数(ペニーでの価格)、およびレシートの説明の30文字。

O(log N)アプローチ:ソートされたリストがあります。ASCIIの場合は44バイト、Unicodeの場合は84バイト。または、UPCをint64として扱うと、42バイトと72バイトが得られます。10,000レコード-最高のケースでは、1メガバイト未満のストレージを見ています。

O(1)アプローチ:UPCを保存せず、代わりに配列へのエントリとして使用します。最も低いケースでは、テラバイトのストレージのほぼ3分の1を表示しています。

どちらの方法を使用するかは、ハードウェアによって異なります。ほとんどの合理的な最新の構成では、log Nアプローチを使用します。何らかの理由でRAMが非常に短いが大容量ストレージが十分にある環境で実行している場合、2番目のアプローチが正しい答えであると想像できます。ディスク上のテラバイトの3分の1は大したことではありません。ディスクの1つのプローブでデータを取得することは、何かの価値があります。単純なバイナリアプローチでは、平均で13になります。(ただし、キーをクラスタ化することで、これを保証された3回の読み取りに減らすことができ、実際には最初の読み取りをキャッシュすることに注意してください。)


2
ここでは少し混乱しています。100億エントリの配列(そのほとんどは未定義)を作成し、UPCをその配列へのインデックスとして扱うことについて話しているのですか。
デビッドZ

7
@DavidZはい。スパース配列を使用すると、O(1)を取得できない場合がありますが、1MBのメモリしか使用しません。実際のアレイを使用する場合、O(1)アクセスが保証されますが、1/3 TBのメモリを使用します。
Navin、2015

最近のシステムでは、1/3 TBのアドレススペースを使用しますが、これは、割り当てられたバッキングメモリの近くに到達するという意味ではありません。最近のほとんどのOSは、必要になるまで割り当て用のストレージをコミットしません。これを行う場合、OS /ハードウェア仮想メモリシステム内のデータの連想ルックアップ構造を本質的に隠しています。
Phil Miller

@Novelocrat True、ただし、RAM速度で実行している場合、ルックアップ時間は重要ではありません。1MBではなく40MBを使用する理由はありません。アレイバージョンは、ストレージへのアクセスにコストがかかる場合にのみ意味があります。
Loren Pechtel

1
または、これがパフォーマンスが重要な操作ではなく、開発者の時間がかかる場合- malloc(search_space_size)返されるものに添え字を付けるのは簡単です。
Phil Miller

36

赤黒木を考えてみましょう。へのアクセス、検索、挿入、削除ができO(log n)ます。にアクセスできる配列と比較してください。O(1)残りの操作はO(n)です。

そのため、アクセスするよりも頻繁に挿入、削除、または検索するアプリケーションと、これら2つの構造のみから選択する場合、赤黒ツリーを選択します。この場合、赤黒木のより面倒なO(log n)アクセス時間を好むと言うかもしれません。

どうして?アクセスは私たちの最優先事項ではないためです。私たちはトレードオフを行っています。アプリケーションのパフォーマンスは、これ以外の要因により大きく影響されます。他のアルゴリズムを最適化することで大きな利益を得るので、この特定のアルゴリズムのパフォーマンスが低下することを許可します。

したがって、あなたの質問に対する答えは単にこれです:アルゴリズムの成長率が最適化したくないものである場合とき、最適化したいとき何か。他のすべての答えはこれの特別なケースです。ときどき、他の操作の実行時間を最適化します。時々、メモリを最適化します。時々、セキュリティを最適化します。メンテナンス性を最適化することもあります。開発時間を最適化することもあります。アルゴリズムの増加率が実行時間への最大の影響ではないことがわかっている場合、問題になるほど十分に低いオーバーライド定数でさえ、実行時間を最適化しています。(データセットがこの範囲外の場合、最終的に定数を支配するため、アルゴリズムの成長率を最適化します。)すべてにコストがあり、多くの場合、より高い成長率のコストを他のものを最適化するアルゴリズム。


配列をO(1)ルックアップと更新O(n)で使用できるようにする操作が赤黒木にどのように対応するかはわかりません。人々は(少なくとも私は)考えていました。ほとんどの場合、私は最初に赤黒木に対するキーベースのルックアップについて考えます。ただし、配列と一致させるには、インデックスベースのルックアップと挿入時の再インデックスを提供するために、上位ノードのサブノードの量を維持する少し異なる構造にする必要があります。赤黒を使用してバランスを維持できることに同意しますが、対応する操作の詳細について曖昧にしたい場合は、バランスツリーを使用できます。
オニー

@ony赤黒木は、マップ/辞書タイプの構造を定義するために使用できますが、そうである必要はありません。ノードは単なる要素にすることができ、基本的にソートされたリストを実装します。
jpmc26

要素の順序を定義するソートされたリストと配列は、情報量が異なります。1つは要素とセットの間の順序に基づいており、もう1つは要素間の順序を定義する必要のない任意のシーケンスを定義します。他にあなたO(log n)が「赤黒木」であると宣言する「アクセス」と「検索」とは何ですか?挿入5配列の位置2に[1, 2, 1, 4]もたらすであろう[1, 2, 5, 1 4](要素4インデックスが3から4に更新されます)。O(log n)「ソートされたリスト」として参照する「赤黒木」でこの動作をどのように行うのですか?
オニー

@ony "要素の順序を定義するソートされたリストと配列は、情報量が異なります。" はい、そのため、パフォーマンス特性が異なります。あなたは要点を逃しています。1つは、すべての状況において、もう1つの代替品のドロップではありません。彼らはさまざまなこと最適化し、さまざまなトレードオフを行います。ポイントは、開発者がそれらのトレードオフについて絶えず決定を下していることです。
jpmc26

@onyアクセス、検索、挿入、および削除には、アルゴリズムのパフォーマンスのコンテキストで特定の意味があります。アクセスは要素を位置でフェッチしています。検索は要素を値で検索します(これには、非マップ構造の包含チェックとしての実用的なアプリケーションのみがあります)。ただし、挿入と削除は簡単です。使用例はここにあります
jpmc26

23

はい。

実際のケースでは、短い文字列キーと長い文字列キーの両方を使用してテーブルルックアップを実行するテストをいくつか実行しました。

私たちは、使用std::mapstd::unordered_mapハッシュとそのサンプルを最大で10文字列の長さが倍以上(これはまともですので、私達のキーは、GUID-ようになる傾向があり)、およびサンプルすべての文字(理論的には、衝突を軽減)というハッシュ、==比較を行うソートされていないベクトルと、(私が正しく覚えていれば)ハッシュも格納するソートされていないベクトルです。最初にハッシュを比較してから、文字を比較します。

これらのアルゴリズムの範囲は、O(1)(unordered_map)からO(n)(線形検索)までです。

適度なサイズのNの場合、O(n)がO(1)を上回ることがよくあります。これは、ノードベースのコンテナーではコンピューターがメモリ内をより多くジャンプする必要があったのに対し、線形ベースのコンテナーではそうではなかったためと考えられます。

O(lg n)2つの間に存在します。どのようにしたか覚えていません。

パフォーマンスの違いはそれほど大きくなく、大きなデータセットでは、ハッシュベースのデータセットの方がはるかに優れていました。そのため、ハッシュベースの順序付けされていないマップを使用しました。

実際には、妥当なサイズのnの場合O(lg n)O(1)です。お使いのコンピュータのテーブルに40億のエントリしか置けない場合は、O(lg n)は、上にで囲まれ32ます。(lg(2 ^ 32)= 32)(コンピュータサイエンスでは、lgは対数ベース2の省略形です)。

実際には、対数成長係数のためではなく、lg(n)部分がアルゴリズムにある程度の複雑さがあることを意味するため、lg(n)アルゴリズムはO(1)アルゴリズムよりも遅く、その複雑さにより、 lg(n)項からの「成長」のどれよりも大きい定数係数。

ただし、(ハッシュマッピングのような)複雑なO(1)アルゴリズムは、類似またはより大きな定数係数を簡単に持つことができます。


21

アルゴリズムを並行して実行する可能性。

クラスO(log n)との例があるかどうかはわかりませんO(1)が、いくつかの問題では、アルゴリズムの並列実行が簡単な場合に、より複雑なクラスのアルゴリズムを選択します。

一部のアルゴリズムは並列化できませんが、それほど複雑でないクラスです。同じ結果が得られ、簡単に並列化できるが、より複雑なクラスを持つ別のアルゴリズムを考えてみましょう。1台のマシンで実行すると、2番目のアルゴリズムは遅くなりますが、複数のマシンで実行すると、実際の実行時間は次第に短くなり、最初のアルゴリズムは高速化できません。


しかし、その並列化のすべては、他の人が話している定数要素を減らすことですよね?
gengkev

1
はい。ただし、並列アルゴリズムでは、実行中のマシンの数を2倍にするたびに定数係数を2で除算できます。別のシングルスレッドアルゴリズムでは、一定の方法で定数係数を1回だけ削減できます。したがって、並列アルゴリズムを使用すると、nのサイズに動的に反応し、壁時計の実行時間を短縮できます。
模擬

15

組み込みシステムにブラックリストを実装しているとしましょう。0〜1,000,000の数値がブラックリストに登録されている可能性があります。これにより、次の2つのオプションが可能になります。

  1. 1,000,000ビットのビットセットを使用する
  2. ブラックリストに入れられた整数のソートされた配列を使用し、バイナリ検索を使用してそれらにアクセスします

ビットセットへのアクセスは、一定のアクセスが保証されます。時間の複雑さに関しては、最適です。理論的観点と実用的な観点の両方から(非常に低い一定のオーバーヘッドを持つO(1)です)。

それでも、2番目のソリューションを使用することをお勧めします。特に、メモリ効率が向上するため、ブラックリストに登録された整数の数が非常に少ないと予想される場合。

また、メモリが不足している組み込みシステム向けに開発しなくても、1,000,000から1,000,000,000,000の任意の制限を増やして、同じ議論をすることができます。その場合、ビットセットには約125Gのメモリが必要になります。O(1)の最悪の場合の複雑さを保証しても、上司にそのような強力なサーバーを提供するように説得することはできません。

ここでは、O(1)ビットセットよりもバイナリ検索(O(log n))またはバイナリツリー(O(log n))を強く推奨します。そして恐らく、最悪の場合のO(n)の複雑さを持つハッシュテーブルは、実際にはそれらすべてに勝るものです。



12

人々はあなたの正確な質問にすでに答えているので、ここに来るときに人々が実際に考えているかもしれない少し異なる質問に取り組みます。

「O(1)時間」アルゴリズムとデータ構造の多くは、実際には予想される O(1)時間のみを使用します。つまり、平均実行時間はO(1)であり、特定の仮定の下でのみ可能です。

一般的な例:ハッシュテーブル、「配列リスト」の拡張(別名動的サイズの配列/ベクトル)。

このようなシナリオでは、平均してパフォーマンスが低下する場合でも、対数的に絶対的に制限されていることが保証されているデータ構造またはアルゴリズムを使用することをお勧めします。
したがって、例として、実行時間は平均で最悪ですが、最悪の場合はより良いバランスのとれた二分探索ツリーが考えられます。


11

より一般的な質問は、無限に向かう傾向があるとしてもO(f(n))O(g(n))アルゴリズムよりもアルゴリズムを好む状況があるかどうかです。他の人がすでに述べたように、その答えは明らかに「YES」の場合はあると。多項式であるが指数関数的である場合でさえ、それは時々イエスです。有名で重要な例は、線形計画問題を解くためのシンプレックスアルゴリズムの例です。1970年代にはそれが示されました。したがって、その最悪の場合の動作は実行不可能です。しかし、その平均的なケースの振る舞いは、何万もの変数や制約がある実際的な問題であっても、非常に優れています。1980年代には、多項式時間アルゴリズム(このようなg(n) << f(n)nf(n) = log(n)g(n) = 1f(n)g(n)O(2^n)法の Karmarkarの内点法アルゴリズム)線形計画法が発見されましたが、30年後、シンプレックスアルゴリズムは依然として最適なアルゴリズムのようです(特定の非常に大きな問題を除いて)。これは、平均的なケースの振る舞いが悪いケースの振る舞いよりもしばしば重要であるという明白な理由のためですが、シンプレックスアルゴリズムが何らかの意味でより有益である(たとえば、感度情報が抽出しやすい)より微妙な理由もあります。


10

私の2セントを入れるには:

アルゴリズムが特定のハードウェア環境で実行されるときに、より複雑なアルゴリズムがより良いアルゴリズムの代わりに選択されることがあります。O(1)アルゴリズムが非常に大きな固定サイズの配列のすべての要素に非順次的にアクセスして問題を解決するとします。次に、そのアレイを機械的なハードドライブまたは磁気テープに配置します。

その場合、O(logn)アルゴリズム(ディスクに順次アクセスする場合)がより有利になります。


ここで、シーケンシャルアクセスドライブまたはテープでは、代わりにO(1)アルゴリズムがO(n)になることを追加します。これが、シーケンシャルソリューションがより有利になる理由です。多くのO(1)操作は、シーケンシャルアクセススペースではない定数時間アルゴリズムである追加およびインデックス付きルックアップに依存しています。
TheHansinator 2015

9

他の多くの回答が無視したO(1)アルゴリズムの代わりにO(log(n))アルゴリズムを使用する良いユースケースがあります:不変性。ハッシュマップはO(1)のputsとgetsを持ち、ハッシュ値の分布が適切であると想定していますが、変更可能な状態が必要です。不変のツリーマップにはO(log(n))のputsとgetsがあり、漸近的に遅いです。ただし、不変性はパフォーマンスの低下を補うのに十分な価値がある場合があり、マップの複数のバージョンを保持する必要がある場合は、不変性によりマップをコピーする必要がなくなるため、O(n)となり、したがって改善することができます。パフォーマンス。


9

単に:係数(そのステップのセットアップ、ストレージ、および実行時間に関連するコスト)は、大きな問題よりも小さなBig-O問題の方がはるかに大きくなる可能性があるためです。Big-Oは、アルゴリズムのスケーラビリティの単なる尺度です。

量子力学の複数の世界の解釈に依存するソートアルゴリズムを提案する、ハッカーの辞書からの次の例を検討してください:

  1. 量子プロセスを使用してランダムに配列を並べ替え、
  2. 配列がソートされていない場合は、ユニバースを破棄してください。
  3. 残りのすべてのユニバースがソートされます(現在のユニバースを含む)。

(ソース:http : //catb.org/~esr/jargon/html/B/bogo-sort.html

このアルゴリズムのビッグOはであることに注意してください。これはO(n)、一般的なアイテムでこれまでに知られているすべてのソートアルゴリズムより優れています。線形ステップの係数も非常に低くなっています(線形に行われるのは、スワップではなく比較だけなので)。実際、同様のアルゴリズムを使用して、多項式時間でNPco-NPの両方の問題を解決できます。可能な解決策(または解決策がないことの証明)は、量子プロセスを使用して生成でき、多項式時間。

ただし、ほとんどの場合、Multiple Worldsが正しくない可能性があるというリスクを冒したくはないでしょう。言うまでもなく、ステップ2を実装する行為は、依然として「読者のための演習として残されています」。


7

nが制限されていて、O(1)アルゴリズムの定数乗数がlog(n)の制限よりも高い任意の時点。 たとえば、ハッシュセットに値を格納することはO(1)ですが、ハッシュ関数の高価な計算が必要になる場合があります。データアイテムを(ある順序に関して)簡単に比較でき、nの境界がlog nが1つのアイテムのハッシュ計算よりも大幅に少ない場合、バランスのとれたバイナリツリーに格納する方が、ハッシュセット。


6

確かな上限が必要なリアルタイムの状況では、ヒープソートの平均的な動作も最悪の場合の動作であるため、クイックソートではなくヒープソートを選択します。


6

すでに良い答えを追加します。実用的な例は、postgresデータベースのハッシュインデックスとBツリーインデックスです。

ハッシュインデックスはハッシュテーブルインデックスを形成してディスク上のデータにアクセスしますが、btreeはその名のとおり、Btreeデータ構造を使用しています。

Big-O時間では、これらはO(1)対O(logN)です。

実際の状況では、特にデータベースシステムでは、ハッシュインデックスは現在推奨されていません。衝突なしにハッシュを達成することは非常に困難であり(O(N)最悪のケースの複雑さにつながる可能性があります)、このため、作成はさらに困難ですそれらは安全にクラッシュします(書き込み先行ロギングと呼ばれます-postgresではWAL)。

O(logN)はインデックスに対して十分であり、O(1)の実装はかなり難しく、時間差は実際には問題にならないため、このトレードオフはこの状況で行われます。



3
  1. O(1)の「1」作業単位がO(log n)の作業単位に比べて非常に高く、予想されるセットサイズが小さめの場合。たとえば、項目が2つまたは3つしかない場合は、配列を反復処理するよりも辞書のハッシュコードを計算する方がおそらく遅くなります。

または

  1. O(1)アルゴリズムのメモリまたはその他の非時間リソース要件が、O(log n)アルゴリズムに比べて非常に大きい場合。

3
  1. プログラムを再設計すると、プロシージャはO(lgN)ではなくO(1)で最適化されることがわかりますが、それがこのプログラムのボトルネックではなく、O(1)アルゴリズムを理解するのは難しい場合です。次に、O(1)アルゴリズムを使用する必要はありません。
  2. O(1)が提供できない多くのメモリを必要とするとき、O(lgN)の時間は受け入れられます。

1

これは、誰かが問題に対する回答をあまりにも早く取得できないようにするために、アルゴリズムがわざと遅い問題を設計したいセキュリティアプリケーションの場合によく見られます。

ここに私の頭の上のいくつかの例があります。

  • ブルートフォースによるパスワードの推測を困難にするために、パスワードのハッシュが任意に遅くされることがあります。この情報セキュリティの投稿には、それに関する箇条書き(およびその他)があります。
  • ビットコインは、コインを「採掘」するために、コンピューターのネットワークが制御可能な遅い問題を解決するために使用します。これにより、通貨は統制システムによって制御されたレートでマイニングできます。
  • 非対称暗号(RSAなど)は、秘密鍵を持たない誰かが暗号化を解読するのを防ぐために、意図的に鍵を使わずに復号を行うように設計されています。アルゴリズムは、うまくいけばO(2^n)時間内に解読されるように設計されておりn、キーのビット長はどこですか(これはブルートフォースです)。

CSの他の場所では、クイックソートがO(n^2)最悪のケースですが、一般的なケースはO(n*log(n))です。このため、アルゴリズムの効率を分析するときに気にするのは、「Big O」分析だけではありません。

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