web-dev-qa-db-ja.com

O(log N)== O(1)-なぜですか?

アルゴリズム/データ構造を検討するときはいつでも、log(N)部分を定数に置き換える傾向があります。ああ、log(N)が発散することは知っていますが、実際のアプリケーションでは重要ですか?

log(infinity)<100すべての実用的な目的。

私はこれが当てはまらない実世界の例に本当に興味があります。

明確にするために:

  • 私はO(f(N))を理解しています
  • 漸近動作が実際のパフォーマンスの定数よりも重要である実際の例に興味があります。
  • Log(N)を定数で置き換えることができる場合でも、O(N log N)の定数で置き換えることができます。

この質問は、(a)エンターテインメント、および(b)デザインのパフォーマンスに関する論争に(再び)遭遇した場合に使用する引数を収集するためのものです。

45
phoku

これは実用的なアプローチだと思います。 O(logN)は64を超えることはありません。実際には、項がO(logN)のように「小さい」になるたびに、定数係数が勝つかどうかを測定する必要があります。を参照してください。また

アッカーマン関数の使用?

別の回答へのコメントから自分自身を引用するには:

[Big-Oh]「分析」は、少なくともO(N)である要因に対してのみ重要です。小さな要因の場合、big-oh分析は役に立たないため、測定する必要があります。

そして

「O(logN)入力サイズは重要です。」これが質問の要点です。もちろん重要です...理論上。OPが尋ねる質問は、それは重要ですか実際には?答えはノーであると主張します。 logNが常に一定時間のアルゴリズムに勝つほど速く成長するデータセットではなく、今後もそうなることはありません。孫の生涯で考えられる最大の実用的なデータセットであっても、logNアルゴリズムには次のような可能性があります。一定時間のアルゴリズムを打ち負かす-常に測定する必要があります。

編集

良い話:

http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

途中で、Richは明らかにO(logN)であるClojureのハッシュ試行について説明しますが、対数の底が大きいため、40億の値が含まれていても、試行の深さは最大6です。ここで、「6」はまだO(logN)値ですが、非常に小さい値であるため、「本当にO(1)が必要」であるため、このすばらしいデータ構造を破棄することを選択します。愚かなことです。これは、この質問に対する他のほとんどの回答が、アルゴリズムを「」にしたいと考えている実用主義者の観点から、単純に間違っていることを強調しています。 「理論」が何を言っているかに関係なく、「速く走る」と「うまくスケールする」。

編集

も参照してください

http://queue.acm.org/detail.cfm?id=1814327

それは言う

O(log2(n))アルゴリズムがこれらの操作によってページフォールトが発生し、ディスク操作が遅くなる場合、どのような利点がありますか?最も関連性の高いデータセットの場合、O(n)または、ページフォールトを回避するO(n ^ 2)アルゴリズムでさえ、その周りを一周します。

(ただし、コンテキストについては記事を読んでください)。

23
Brian

Big O表記は、入力の増加に伴ってアルゴリズムがどのように変化するかを示します。 O(1)は、入力がどれだけ大きくなるかは問題ではないことを示しています。アルゴリズムは常に同じくらい高速です。O(logn)アルゴリズムは高速ですが、入力が大きくなるにつれて少し時間がかかります。

O(1)とO(logn)は、アルゴリズムの組み合わせを開始するときに大きな違いをもたらします。

たとえば、インデックスを使用して結合を行うとします。 O(1)の代わりにO(logn)で結合を行うことができれば、パフォーマンスが大幅に向上します。たとえば、O(1)何度でも結合でき、O(1)はまだあります。ただし、O(logn)を使用すると、毎回操作カウントにlognを掛ける必要があります。 。

大規模な入力の場合、すでにO(n ^ 2)であるアルゴリズムがある場合は、O(1)内部ではなく、O(logn)内部。

また、Big-Oは一定のオーバーヘッドを持つ可能性があることも忘れないでください。一定のオーバーヘッドが100万であるとしましょう。 O(1)の場合、一定のオーバーヘッドはO(logn)のように操作の数を増幅しません。

もう1つのポイントは、たとえば、ツリーデータ構造のn個の要素を表すO(logn))を誰もが考えていることです。ただし、ファイル内のバイトを含むものであれば何でもかまいません。

60
Brian R. Bondy

これはよくある間違いです。BigO表記は、特定の値でのアルゴリズムの絶対的なパフォーマンスを示すものではなく、入力のサイズを大きくしたときのアルゴリズムの動作を示すだけであることを忘れないでください。

その文脈でそれを取ると、アルゴリズムA〜 O(logN)とアルゴリズムB〜 O(1)アルゴリズムが異なる理由が明らかになります:

サイズaの入力でAを実行し、次にサイズ1000000 * aの入力で実行すると、2番目の入力は最初の入力のlog(1,000,000)倍の時間がかかると予想できます。

サイズaの入力でBを実行し、次にサイズ1000000 * aの入力で実行すると、2番目の入力に最初の入力とほぼ同じ時間がかかることが予想されます。

[〜#〜] edit [〜#〜]:あなたの質問をもう少し考えてみると、そこにはいくつかの知恵があると思います。 O(lgN) == O(1)と言うのが正しいとは決して言えませんが、それは[〜#〜] is [〜#〜]可能ですO(lgN)アルゴリズムはO(1)アルゴリズムよりも使用される可能性があります。これは、上記の絶対的なパフォーマンスに関するポイントに戻ります。1つのアルゴリズムを知っているだけです。はO(1)であり、別のアルゴリズムはO(lgN)は[〜#〜] not [〜#〜] O(lgN)に対してO(1)を使用する必要があることを宣言するのに十分です。可能な入力の範囲があれば、それは確かに可能ですO(lgN)あなたに最も役立つかもしれません。

20
Falaina

あなたは実際の例を求めました。あげます。計算生物学。 ASCIIでエンコードされたDNAの1つのストランドは、空間のギガバイトのレベルのどこかにあります。一般的なデータベースには、明らかに何千ものそのようなストランドがあります。

さて、索引付け/検索アルゴリズムの場合、そのlog(n)倍数は、定数と組み合わせると大きな違いを生みます。理由は?これは、入力のサイズが天文学的なアプリケーションの1つです。さらに、入力サイズは常に大きくなり続けます。

確かに、この種の問題はまれです。これほど大きなアプリケーションは非常に多くあります。しかし、そのような状況では...それは違いの世界を作ります。

7
San Jacinto

Nが十分に小さい場合、O(N ^ N)は実際には1に置き換えることができます。O(1)(定義による)ではありませんが、N = 2の場合、4つの部分からなる1つの操作として見ることができます。一定時間の操作。

すべての操作に1時間かかる場合はどうなりますか?その場合、O(log N)とO(1)の差は、Nが小さくても大きくなります。

または、アルゴリズムを1,000万回実行する必要がある場合はどうでしょうか。わかりました。30分かかりました。100倍の大きさのデータセットで実行すると、O(logN)はO(1)と「同じ」であるため、30分かかるはずです。 。何?

「私はO(f(N))を理解している」というあなたの発言は明らかに誤りです。

実世界のアプリケーション、ああ...わかりません.... O()のすべての使用-表記はこれまでですか?

たとえば、1,000万アイテムのソート済みリストでのバイナリ検索。データが十分に大きくなったときにハッシュテーブルを使用するのは、まさにその理由です。 O(logN)がO(1)と同じだと思うなら、なぜ二分木の代わりにハッシュを使用するのでしょうか。

5
Thomas

多くの人がすでに言っているように、現実の世界では、O(log N)の因子について心配する前に、まず定数因子を調べる必要があります。

次に、Nがどうなるかを考えます。 N <10と考える十分な理由がある場合は、バイナリ検索の代わりに線形検索を使用できます。これは、O(log N)の代わりにO(N)です。これは、ライトによるとwould重要です-しかし、見つかった要素を前に移動する線形検索です。より複雑なバランスツリーよりもパフォーマンスが優れている可能性がありますアプリケーションによって異なります

一方、log Nが50を超える可能性が低い場合でも、パフォーマンス係数10は非常に大きいことに注意してください。計算に縛られている場合、そのような係数はアプリケーションを簡単に作成または破壊する可能性があります。それだけでは不十分な場合は、アルゴリズムに(log N)^ 2または(logN)^ 3の因数が頻繁に表示されるため、(log N)の1つの因数を無視できると思っても、それは意味しません。あなたはそれらの多くを無視することができます。

最後に、線形計画法のシンプレックスアルゴリズムのパフォーマンスは最悪の場合O(2 ^ n)であることに注意してください。ただし、実際の問題の場合、最悪のケースは発生しません。実際には、シンプレックスアルゴリズムは高速で、比較的単純であるため、非常に人気があります。

約30年前、誰かが線形計画法の多項式時間アルゴリズムを開発しましたが、結果が遅すぎるであったため、最初は実用的ではありませんでした。

今日では、線形計画法の実用的な代替アルゴリズムがあり(多項式時間の最悪の場合、それだけの価値があります)、実際にはシンプレックス法よりも優れたパフォーマンスを発揮します。しかし、問題によっては、シンプレックス法は依然として競争力があります。

5
comingstorm

平等、つまりあなたがそれを説明する方法は、表記法の一般的な乱用です。

明確にするために、私たちは通常f(x) = O(logN) "f(x)is O(logN)"を意味します。

いずれにせよ、O(1)は、入力セットの大きさに関係なく、アクションを実行するための一定のステップ/時間(上限として)を意味します。ただし、O(logN)の場合、ステップ数/時間は入力サイズ(その対数)の関数として増加しますが、非常にゆっくりと増加します。ほとんどの実際のアプリケーションでは、このステップ数が100を超えないと想定しても安全ですが、ステートメントを危険と無効の両方としてマークするのに十分な大きさのデータセットの例が複数あると思います(パケットトレース、環境測定、さらに多く)。

5

O(log n)O(1)と見分けがつかないことがよくあるという観察は良いものです。

おなじみの例として、1つの1,000,000,000,000要素の並べ替えられた配列から1つの要素を見つけたいとします。

  • 線形検索では、検索には平均500,000,000,000ステップかかります
  • 二分探索では、検索は平均40ステップかかります

検索している配列に単一の要素を追加し、次に別の要素を検索する必要があるとします。

  • 線形検索では、検索には平均500,000,000,001ステップがかかります(区別できない変化)
  • 二分探索では、検索は平均40ステップかかります(見分けがつかない変化)

検索している配列の要素数を2倍にしたとします。次に、別の要素を検索する必要があります。

  • 線形検索では、検索には平均1,000,000,000,000ステップかかります(非常に顕著な変化)
  • 二分探索では、検索は平均41ステップかかります(見分けがつかない変化)

この例からわかるように、すべての意図と目的で、バイナリ検索のようなO(log n)アルゴリズムは、全知のようなO(1)アルゴリズムと区別できないことがよくあります。

要点は次のとおりです。* O(log n)アルゴリズムを使用するのは、それらが一定時間と区別できないことが多く、線形時間アルゴリズムよりも驚異的に優れていることが多いためです。

明らかに、これらの例は妥当な定数を想定しています。明らかに、これらは一般的な観察であり、すべての場合に当てはまるわけではありません。明らかに、これらの点は、_n=3_の終わりではなく、曲線の漸近的な終わりに適用されます。

しかし、この観察結果は、たとえば、クエリを調整してテーブルスキャンではなくインデックスシークを実行する理由を説明しています-インデックスシークはデータセットのサイズに関係なくほぼ一定の時間で動作しますが、テーブルスキャンは十分に大きなデータセットでは非常に遅くなります。インデックスシークはO(log n)です。

4
yfeldblum

対数コストを無視するSoft-Oに興味があるかもしれません。ウィキペディアで この段落 を確認してください。

3
sdcvvc

質問のタイトルは誤解を招くものです(議論を盛り上げるためによく選ばれています、気をつけてください)。

O(log N)== O(1)は明らかに間違っています(そして投稿者はこれを認識しています)。定義上、Big O表記は漸近解析を考慮します。O(N )、Nは無限大に近づくように取られます。Nに定数が割り当てられている場合、それはBigOではありません。

これは、理論計算機科学者だけが気にする必要のある、ちょっとした詳細ではないことに注意してください。アルゴリズムのO関数を決定するために使用されるすべての算術は、それに依存しています。アルゴリズムのO関数を公開するとき、そのパフォーマンスに関する情報のlotを省略している可能性があります。

Big O分析は、プラットフォーム固有の問題(ワードサイズ、操作ごとの命令、メモリ速度とディスク速度)にとらわれることなくアルゴリズムを比較できるため、優れています。 Nが無限大になると、これらの問題はなくなります。しかし、Nが10000、1000、100の場合、これらの問題は、O関数から除外した他のすべての定数とともに問題になり始めます。

ポスターの質問に答えるには:O(log N)!= O(1)、そしてあなたが正しいです、O(1)のアルゴリズムは、Oのアルゴリズムよりもはるかに優れていない場合があります(log N)、入力のサイズ、およびBigO分析中に省略されたすべての内部定数によって異なります。

Nをクランクアップすることがわかっている場合は、BigO分析を使用します。そうでない場合は、いくつかの経験的テストが必要になります。

2
Scott A Miller

それが「重要」であるかどうかはどういう意味ですか?

O(1)アルゴリズムとO(lg n)アルゴリズムの選択に直面している場合は、それらが等しいと想定すべきではありません。一定時間のものを選択する必要があります。どうしてそうしませんか?

また、一定時間のアルゴリズムが存在しない場合は、通常、対数時間のアルゴリズムが最適です。繰り返しますが、それではmatter?あなたはただあなたが見つけることができる最も速いものを取る必要があります。

2つを等しいと定義することで何かが得られる状況を教えていただけますか?せいぜい、それは違いをもたらさないでしょう、そして最悪の場合、あなたはいくつかの本当のスケーラビリティ特性を隠すでしょう。通常、定数時間アルゴリズムwillは対数アルゴリズムよりも高速であるためです。

あなたが言うように、すべての実用的な目的のためにlg(n) < 100であっても、それはあなたの他のオーバーヘッドの上にまだ100倍です。関数をN回呼び出すと、関数が対数時間で実行されるか定数で実行されるかが問題になります。これは、全体の複雑さがO(n lg n)またはO(n)であるためです。

ですから、「現実の世界」では対数の複雑さが一定であると仮定することが「重要」であるかどうかを尋ねるのではなく、そうすることに意味があるかどうかを尋ねます。

多くの場合、対数アルゴリズムは十分に高速であると想定できますが、それらを一定と見なすことで何が得られますか?

2
jalf

O(logN)*O(logN)*O(logN) is very different. O(1) * O(1) * O(1) is still constant. Also a simple quicksort-style O(nlogn) is different than O(n O(1))=O(n). Try sorting 1000 and 1000000 elements. The latter isn't 1000 times slower, it's 2000 times, because log(n^2)=2log(n)

2
user181060

理論上

はい、実際の状況では、log(n)は定数で制限され、100と言います。ただし、正しい状況でlog(n)を100に置き換えると、情報が破棄され、操作の上限が決まります。計算が緩く、あまり役に立たない。分析でO(log(n))をO(1)に置き換えると、大きなnケースのパフォーマンスが予想より100倍悪くなる可能性があります。小さなnの場合に基づいています。理論的な分析はより正確であり、システムを構築する前に問題を予測できた可能性があります。

Big-O分析の実際的な目的は、アルゴリズムの実行時間をできるだけ早く予測することであると私は主張します。 log(n)の項に取り消し線を引くことで分析を容易にすることができますが、その場合、推定の予測力が低​​下します。

実際には

Googleアーキテクチャに関するLarryPageとSergeyBrinの元の論文を読んだ場合、彼らはすべてにハッシュテーブルを使用して、たとえばキャッシュされたWebページのルックアップには、ハードディスクのシークが1回だけ必要です。 Bツリーインデックスを使用してルックアップした場合、キャッシュされていないルックアップを実行するために4つまたは5つのハードディスクシークが必要になる場合があります[*]。キャッシュされたWebページストレージのディスク要件を4倍にすることは、ビジネスの観点から気にする価値があり、すべてのO(log(n))用語をキャストしない場合は予測可能です。

P.S.例としてGoogleを使用して申し訳ありませんが、それらはコンピュータサイエンスバージョンのヒトラーのようなものです ゴドウィンの法則

[*]ディスクからの読み取りが4KB、インデックスに1,000億のWebページ、Bツリーノードのキーごとに約16バイトと仮定します。

2
Alex

アプリケーション全体で、ユーザーが最も一般的な操作を待機する時間の90%を1つのアルゴリズムが占めると想定します。

リアルタイムでO(1)操作がアーキテクチャで1秒かかり、O(logN)操作が基本的に.5秒* log( N)さて、この時点で、曲線と線の交点に「ここで重要です」という矢印の付いたグラフを描きたいと思います。log(N)opを使用します。このようなシナリオでは、小さなデータセットの場合はO(1)大規模なデータセットの場合はop)。

Big-O表記法とパフォーマンスの最適化は、すでに安価な操作に対してユーザーに真の価値を提供するのではなく、学術的な演習ですが、クリティカルパスでの高価な操作である場合は、間違いなく重要です。

1
Chris Moschini

Big-OH​​は、一定の係数が与えられた場合、あるアルゴリズムが別のアルゴリズムよりも高速であることを示しています。入力が十分に小さい定数係数を意味する場合は、ある塩基のlog(n)検索ではなく、線形検索を使用することで、パフォーマンスが大幅に向上する可能性があります。

1
CLF

他の人が指摘しているように、Big-Oは、問題のパフォーマンスがどのように拡大するかについて説明します。私を信じてください-それは重要です。アルゴリズムがひどくて、遅すぎて顧客の要求に応えられなかったアルゴリズムに何度か遭遇しました。違いを理解し、O(1)ソリューションを見つけることは、多くの場合、大きな改善です。

ただし、もちろん、それだけではありません。たとえば、小さなデータセットでの両方のアルゴリズムの動作により、クイックソートアルゴリズムは常に小さな要素の挿入ソートに切り替わることに気付くかもしれません(ウィキペディアによると8-20)。

したがって、問題、アーキテクチャ、および使用するものを理解するための経験、および関連する定数を調整する方法を完全に理解することを含む、実行するトレードオフを理解することが重要です。

O(1)が常にO(log N)よりも優れているとは誰も言っていません。ただし、O(1)アルゴリズムまた、スケーリングが大幅に向上するため、システムにユーザーが何人いるか、または処理するデータのサイズについて誤った仮定をした場合でも、アルゴリズムには関係ありません。

1
Vitali

異なるサイズNの入力を受け取ることができるアルゴリズムの場合、取る操作の数は、ある関数f(N)によって上限が定められます。

すべてのbig-Oは、その関数の形であることを示しています。

  • O(1)は、大きなNに対してf(N) <Aとなるような数Aがあることを意味します。

  • O(N)は、大きなNに対してf(N) <ANとなるようなAがあることを意味します。

  • O(N ^ 2)は、大きなNに対してf(N) <AN ^ 2となるようなAがあることを意味します。

  • O(log(N))は、f(N) <大きなNのAlogNとなるようなAがあることを意味します。

Big-Oは、Aの大きさ(つまり、アルゴリズムの速度)や、これらの関数が互いに交差する場所については何も述べていません。 2つのアルゴリズムを比較しているときに、それらのbig-Oが異なる場合、一方のアルゴリズムが他方よりもパフォーマンスが向上し始めるNの値(小さい場合もあれば非常に大きい場合もある)があることだけを示しています。

1
Mike Dunlavey

O(log n)= O(1)と決定しない場合、Big-O表記を決定するルールはより単純になります。

Krzysioが言ったように、O(log n)を蓄積すると、非常に顕著な違いが生じます。二分探索を行うと想像してください:O(log n)比較、そして各比較の複雑さO(log n)を想像してください。両方を無視すると、O(logの代わりにO(1)2n)。同様に、どういうわけかO(log10n)すると、「n」が大きすぎない場合に大きな違いが見られます。

1
yairchu

あなたは正しいです、多くの場合、それは実際的な目的には関係ありません。しかし、重要な問題は「どれだけ速くGROWSNになるか」です。私たちが知っているほとんどのアルゴリズムは入力のサイズを取るので、それは直線的に大きくなります。

ただし、一部のアルゴリズムでは、複雑な方法でNの値が導出されます。 Nが「X個の異なる番号を持つ宝くじの可能な宝くじの組み合わせの数」である場合、アルゴリズムがO(1)またはO(logN)であるかどうかが突然重要になります。

1

はい、ほとんどの実用的な目的でlog(N)<100です。いいえ、常に定数に置き換えることはできません。

たとえば、これにより、プログラムのパフォーマンスを見積もる際に重大なエラーが発生する可能性があります。 O(N)プログラムが1ミリ秒で1000要素の配列を処理した場合、10を処理すると確信しています6 1秒(またはそれくらい)の要素。ただし、プログラムがO(N * logN)の場合、10を処理するのに約2秒かかります。6 要素。この違いは非常に重要な場合があります。たとえば、1時間あたり3000リクエストを受信し、サーバーが最大3600を処理できるため、十分なサーバーパワーがあると考える場合があります。

もう一つの例。関数f() O(logN)で動作し、各反復で関数g()を呼び出すと、O(logN) asで動作するとします。次に、両方のログを定数に置き換えると、プログラムは一定の時間で動作すると思いますが、現実は残酷です-2つのログで最大100 * 100の乗数が得られる可能性があります。

1
Olexiy

O(log N)は誤解を招く可能性があります。たとえば、 赤黒木 の操作を考えてみましょう。
操作はO(logN)ですが、かなり複雑です。つまり、多くの低レベルの操作を意味します。

0