O(log n)は正確にはどういう意味ですか?


2139

Big O Notationの実行時間と償却時間について学習しています。私はO(n)線形時間の概念を理解しています。つまり、入力のサイズがアルゴリズムの成長に比例して影響を及ぼします...たとえば、二次時間O(n 2)などでも同じです、階乗によって成長するO(n!)回の置換ジェネレータなど。

たとえば、次の関数はO(n)です。これは、アルゴリズムが入力nに比例して大きくなるためです。

f(int n) {
  int i;
  for (i = 0; i < n; ++i)
    printf("%d", i);
}

同様に、ネストされたループがある場合、時間はO(n 2)になります。

しかし、正確にはO(log n)とは何ですか?たとえば、完全な二分木の高さがO(log n)であるとはどういう意味ですか?

ログ:私は対数はという意味で、あるもの(そうでないかもしれない非常に詳細で)知っている10 100 = 2を、私は対数時間で機能を特定する方法を理解することはできません。


60
1ノードのバイナリツリーの高さはlog2(1)+1 = 1、2ノードのツリーの高さはlog2(2)+1 = 2、4ノードのツリーの高さはlog2(4)+1 = 3、そしてなど。nノードツリーの高さはlog2(n)+1であるため、ツリーにノードを追加すると、平均の高さが対数的に増加します。
David R Tribble

36
ほとんどの回答で私が見ていることの1つは、本質的に「O(何か)」を説明しているということです。つまり、アルゴリズムの実行時間は「何か」に比例して増加します。「O(log n)」の「正確な意味」を要求したとすると、それは真実ではありません。これはBig-Oではなく、Big-Theta表記の直感的な説明です。O(ログn)は直感的に実行している時間が増える意味せいぜい:比例の「nログ」にstackoverflow.com/questions/471199/...
Mehrdad Afshari

31
私はいつも(Nログ)Oのための例として、分割統治を覚え
RichardOD

14
対数の底が2(底が10ではない)であることを理解することが重要です。これは、アルゴリズムの各ステップで、残りの選択肢の半分を削除するためです。コンピュータサイエンスでは、定数を無視できるため、ほとんどの場合、対数2を扱います。ただし、いくつかの例外があります(つまり、クワッドツリーの実行時間はログベース4です)
2013年

13
@イーサン:底の変換は定数の乗算なので、どの底にいるかは関係ありません。式はlog_b(x)= log_d(x)/ log_d(b)です。Log_d(b)は定数です。
mindvirus 2013年

回答:


2711

ログ時間で関数を特定する方法がわかりません。

対数ランタイム関数の最も一般的な属性は次のとおりです。

  • いくつかのアクションを実行する次の要素の選択は、いくつかの可能性の1つです。
  • 1つだけ選択する必要があります。

または

  • アクションが実行される要素はnの数字です

これが、たとえば、電話帳で人を検索することがO(log n)である理由です。電話帳のすべての人を調べて適切な人を見つける必要はありません。代わりに、名前がアルファベット順になっている場所に基づいて単純に分割統治することができます。すべてのセクションで、最終的に誰かの電話番号を見つける前に、各セクションのサブセットを探索するだけで済みます。

もちろん、電話帳のサイズが大きいほど時間がかかりますが、追加のサイズが比例して大きくなるほど速くは成長しません。


電話帳の例を拡張して、他の種類の操作とその実行時間を比較できます。電話帳には、一意の名前を持つビジネス(「イエローページ」)と一意の名前を持たない可能性のある人物(「ホワイトページ」)があると想定します。電話番号は多くても1人または1つのビジネスに割り当てられます。また、特定のページに移動するのに一定の時間がかかると仮定します。

以下は、電話帳で実行する可能性があるいくつかの操作の実行時間です(最速から最遅まで)。

  • O(1)(最悪の場合):お店の名前が表示されているページとお店の名前を指定して、電話番号を見つけます。

  • O(1)(平均的なケース):人の名前が表示されているページとその名前を考慮して、電話番号を見つけます。

  • O(log n):人の名前を指定して、まだ検索していない本の部分の半分くらいのところからランダムなポイントを選び、その人の名前がそのポイントにあるかどうかを確認して、電話番号を見つけます。次に、本の人の名前のある部分のほぼ半分までこのプロセスを繰り返します。(これは人の名前のバイナリ検索です。)

  • O(n):電話番号に数字「5」が含まれているすべての人を検索します。

  • O(n):電話番号を指定して、その番号の人または企業を検索します。

  • O(n log n):プリンターのオフィスで取り違えがあり、電話帳にはすべてのページがランダムな順序で挿入されていました。各ページの名を確認し、そのページを新しい空の電話帳の適切な場所に配置して、正しいように順序を修正します。

以下の例では、私たちは今プリンタのオフィスにいます。電話帳は各居住者または企業に郵送されるのを待っており、各電話帳には、どこに郵送するかを示すステッカーが貼られています。すべての人または企業が1つの電話帳を取得します。

  • O(n log n):電話帳をパーソナライズしたいので、指定されたコピーで各個人または企業の名前を検索し、次に本の中でそれらの名前を丸で囲み、後援者に短いお礼状を書きます。 。

  • O(n 2):オフィスでミスが発生し、各電話帳のすべてのエントリに電話番号の末尾に「0」が追加されています。ホワイトアウトをいくつか取り、各ゼロを削除します。

  • O(n・n!):電話帳を出荷ドックにロードする準備ができました。残念ながら、本をロードすることになっていたロボットが問題を抱えています。本をランダムな順序でトラックに載せています!さらに悪いことに、すべての本をトラックに積み込んでから、それらが正しい順序になっているかどうかを確認し、そうでない場合は、積み下ろして最初からやり直します。(これは恐ろしいbogoソートです。)

  • O(n n):ロボットが物を正しくロードするように修正します。翌日、同僚の1人がいたずらをして、荷受ロボットを自動印刷システムに接続します。ロボットが元の本を読み込もうとするたびに、ファクトリープリンターはすべての電話帳の複製を実行します。幸いなことに、ロボットのバグ検出システムは非常に高度で、ロボットがロードする複製本に遭遇したときにさらにコピーを印刷しようとはしませんが、印刷されたすべてのオリジナルおよび複製の本をロードする必要があります。


81
@cletus:偶然です。恐れています。電話帳には大きなNがあり、人々は彼らが何で何をしているのかを理解しているため、そして例として多目的であるため、それを選びました。さらに、説明にはロボットを使用する必要がありました。オールラウンドで勝利。(また、私がStackOverflowのメンバーになる前から回答があったようです!)
John Feminella

12
「オフィスでミスが発生し、各電話帳のすべてのエントリに電話番号の末尾に「0」が追加されています。ホワイトアウトを取り、各ゼロを削除してください。」<-これは次数Nの二乗ではありません。Nは入力のサイズとして定義されます。入力のサイズは、電話番号の数です。これは、本あたりの数に本の数を掛けた数です。それはまだ線形時間操作です。
ビリーONeal

21
@Billy:この例でNは、1冊の本の人数です。電話帳のすべての人も自分の本のコピーを取得するため、それぞれに人がいるN 同一の電話帳があり、NO(N ^ 2)です。
John Feminella、2010

48
奇妙に強調されているので、最悪のケースではなく、O(1)が最良のケースではありませんか?
Svip 2013年

54
最終的に意味のあるO(log n)定義を見つけるのにO(long⅝n!n-55 / 2)時間かかりました。+1
iAteABug_And_iLiked_it 2013

611

O(log N)基本的に、時間は直線的に増加し、n指数関数的に増加します。したがって、要素の1計算に2秒かかる場合10、要素の計算には2数秒、100要素の計算には3数秒という1000ようになります。

それはO(log n)、たとえば二分探索などのタイプのアルゴリズムを分割して征服するときです。別の例は、配列を2つの部分に分割するO(N)たびに、ピボット要素を見つけるのに時間がかかるクイックソートです。したがって、それは N O(log N)


108
他のすべてのエッセイの答えに勝る3行の知恵... :)誰かがそれを逃している場合に備えて、プログラミングコンテキストでは、ログのベースは2(10ではなく)なので、O(log n)は10秒間で1秒のようにスケーリングします要素、20秒は2秒、40秒は3秒など
nawfal 2014年

3
同意、簡潔、明確、ただしOPの最後の質問は対数関数を特定する方法であり、「それは何か」ではない
Adam

4
はい、対数関数は指数関数の逆関数です。((log x)base a)は(power x)の逆です。これらの関数をグラフで定性的に分析すると、より直感的になります。
2014年

7
これは、それが間違っていないことを理解するのに約3回のリードスルーが必要でした。要素は指数関数的ですが、時間は直線的に増加します。これは、より少ない時間でより多くの要素を意味する。これは、グラフでおなじみの対数曲線として視覚化する人にとっては精神的に負担になります。log
Qix-モニカは2017

1
これは、バイナリ検索が分割統治アルゴリズムであると主張している部分を除いて、非常に良い答えだと思います。そうではありません。
code_dredd 2017

579

この質問には多くの良い答えが既に投稿されていますが、私は本当に重要なもの、つまり図解された答えが欠けていると思います。

完全な二分木の高さがO(log n)であるとはどういう意味ですか?

次の図は、バイナリツリーを示しています。各レベルに含まれるノードの数が、上のレベル(したがって、バイナリ)と比較して2倍になることに注目してください。

二分木

二分探索は複雑な例O(log n)です。図1のツリーの最下位レベルにあるノードが、ソートされたコレクションのアイテムを表しているとしましょう。二分探索は分割統治アルゴリズムであり、この図は、この16項目のデータセットで検索しているレコードを見つけるために(最大で)4つの比較が必要になる方法を示しています。

代わりに、32要素のデータセットがあると仮定します。上の図を続けると、データ量を乗算したときにツリーが1レベルだけ深くなっているため、検索対象を見つけるために5つの比較が必要になることがわかります。その結果、アルゴリズムの複雑さは対数次数として説明できます。

log(n)普通の紙にプロットすると、グラフの結果として、曲線の上昇がn増加するにつれて減速します。

O(log n)


60
「各レベルに、上のレベルと比較して2倍の数のノードが含まれていることに注意してください(したがってバイナリ)」これは誤りです。あなたが説明しているのは、バランスのとれた二分木です。バイナリツリーは、各ノードに最大2つの子があることを意味します。
オエノトリア2013年

8
実際、これは完全なバイナリツリーと呼ばれる非常に特殊なバランスのとれたバイナリツリーです。回答を編集しましたが、誰かに承認してもらう必要があります。
user21820

5
完全な二分木は、最後のレベルが完全に満たされる必要はありません。私は、「完全な二分木」がより適切だと思います。
AJ氏2014

あなたの答えは、OPの元の問題に対してより具体的に応答しようとするため、現在受け入れられている答え(IMO)よりは優れていますが、それでも非常に不完全です:半分の例と2つの画像を与えるだけです...
nbro

2
このツリーには、16ではなく31アイテムが含まれています。なぜ16アイテムデータセットと呼ばれるのですか?P:その上のすべてのノードは、そうでなければ、それは非効率的なバイナリツリーであろう、数を表し、
ペリーモンシャウ

245

以下の説明では、完全にバランスのとれたバイナリツリーのケースを使用して、対数時間の複雑さを理解する方法を説明しています。

二分木は、サイズ1の問題に到達するまで、サイズnの問題がサイズn / 2の副問題に分割される場合です。

二分木の高さ

そして、それがO(log n)を取得する方法です。これは、ソリューションに到達するために上記のツリーで実行する必要がある作業量です。

O(log n)時間の複雑さを伴う一般的なアルゴリズムは、二分探索であり、その再帰的関係はT(n / 2)+ O(1)です。つまり、ツリーの後続のすべてのレベルで、問題を半分に分割し、一定量の追加作業を行います。


2
初心者はこちら。ツリーの高さは、サイズn = 1に到達するための再帰による分割率と言えますか?
コーディ、

@コーディ、はい、ほとんどの場合、あなたの観察は正確です。この例では、を示していますlog_2。あなたの観察は超えて消費うlog_2といずれについても正確になるlog_xところx > 1。ただし、まっすぐな除算を行うと正確に1にならない場合があるためCeiling()、最新の除算のが1になるか、または類似するまで、再帰的な除算を言いたい場合があります。
James Oravec

198

概観

他の人は、ツリー図などの良い図の例を示しています。簡単なコード例は見ませんでした。そのため、私の説明に加えて、さまざまなアルゴリズムカテゴリの複雑さを示すために、いくつかのアルゴリズムに単純な印刷ステートメントを提供します。

最初に、Logarithmの一般的な概念が必要になります。これは、https://en.wikipedia.org/wiki/Logarithmから取得できます。自然科学の使用eと自然ログ。コンピューターはバイナリベースであるため、工学の弟子はlog_10(ログベース10)を使用し、コンピューターの科学者はlog_2(ログベース2)を頻繁に使用します。自然対数の省略形がと表示される場合がありますがln()、エンジニアは通常log()_10をオフにしてそのまま使用し、log_2はと省略されlg()ます。すべてのタイプの対数は同様の方法で増加しますlog(n)。そのため、これらは同じカテゴリのを共有します。

以下のコード例を見るときは、O(1)、O(n)、O(n ^ 2)の順に見ることをお勧めします。あなたがそれらに満足したら、次に他の人を見てください。微妙な変更によって同じ分類がどのように行われるかを示すために、明確な例とバリエーションを含めました。

O(1)、O(n)、O(logn)などは、成長のクラスまたはカテゴリと考えることができます。一部のカテゴリは、他のカテゴリよりも実行に時間がかかります。これらのカテゴリは、アルゴリズムのパフォーマンスを順序付ける方法を提供します。一部は、入力nが大きくなるにつれて速く成長しました。次の表は、この成長を数値で示しています。次の表では、log(n)をlog_2の上限と考えています。

ここに画像の説明を入力してください

さまざまなビッグOカテゴリの簡単なコード例:

O(1)-一定時間の例:

  • アルゴリズム1:

アルゴリズム1はhelloを1回出力し、nに依存しないため、常に一定の時間で実行されるため、ですO(1)

print "hello";
  • アルゴリズム2:

アルゴリズム2はhelloを3回出力しますが、入力サイズには依存しません。nが大きくなっても、このアルゴリズムは常にhelloを3回だけ出力します。言われている3は定数なので、このアルゴリズムもO(1)です。

print "hello";
print "hello";
print "hello";

O(log(n))-対数の例​​:

  • アルゴリズム3-これは「log_2」のように機能します

アルゴリズム3は、log_2(n)で実行されるアルゴリズムを示しています。forループのポスト操作は、iの現在の値を2倍するためi、1から2、4から8、16から32になります。

for(int i = 1; i <= n; i = i * 2)
  print "hello";
  • アルゴリズム4-これは「log_3」のように機能します

アルゴリズム4はlog_3を示しています。通知iは1から3から9から27に進みます...

for(int i = 1; i <= n; i = i * 3)
  print "hello";
  • アルゴリズム5-これは「log_1.02」のように機能します

アルゴリズム5は重要です。これは、数値が1より大きく、結果がそれ自体に対して繰り返し乗算される限り、対数アルゴリズムを調べていることを示すのに役立ちます。

for(double i = 1; i < n; i = i * 1.02)
  print "hello";

O(n)-線形時間の例:

  • アルゴリズム6

このアルゴリズムは単純で、helloをn回出力します。

for(int i = 0; i < n; i++)
  print "hello";
  • アルゴリズム7

このアルゴリズムは、helloをn / 2回出力するバリエーションを示しています。n / 2 = 1/2 * n。1/2定数を無視し、このアルゴリズムがO(n)であることを確認します。

for(int i = 0; i < n; i = i + 2)
  print "hello";

O(n * log(n))-nlog(n)の例:

  • アルゴリズム8

組み合わせと考えるO(log(n))O(n)。forループの入れ子は、O(n*log(n))

for(int i = 0; i < n; i++)
  for(int j = 1; j < n; j = j * 2)
    print "hello";
  • アルゴリズム9

アルゴリズム9はアルゴリズム8に似ていますが、各ループでバリエーションが許可されているため、最終的な結果は次のようになります。 O(n*log(n))

for(int i = 0; i < n; i = i + 2)
  for(int j = 1; j < n; j = j * 3)
    print "hello";

O(n ^ 2)-nの2乗の例:

  • アルゴリズム10

O(n^2) ループの標準をネストすることで簡単に取得できます。

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j++)
    print "hello";
  • アルゴリズム11

アルゴリズム10に似ていますが、いくつかのバリエーションがあります。

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j = j + 2)
    print "hello";

O(n ^ 3)-nの3乗の例:

  • アルゴリズム12

これはアルゴリズム10に似ていますが、ループが2ではなく3つあります。

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j++)
    for(int k = 0; k < n; k++)
      print "hello";
  • アルゴリズム13

アルゴリズム12と同様ですが、まだ生成されるいくつかのバリエーションがありますO(n^3)

for(int i = 0; i < n; i++)
  for(int j = 0; j < n + 5; j = j + 2)
    for(int k = 0; k < n; k = k + 3)
      print "hello";

概要

上記は、いくつかの簡単な例と、分析に実際には影響を与えないわずかな変更を導入できることを示すバリエーションです。うまくいけば、十分な洞察が得られます。


17
驚くばかり。私にとって今まで見た中で最も良い説明。場合、それはよりよいだろうO(n^2)との組み合わせとして注目されているO(n)O(n)そう、O(n) * O(n) = O(n * n) = O(n^2)。この方程式がないと、少しジャンプしたように感じられます。これは、前の説明の繰り返しですが、この繰り返しは、読者が理解するためのより多くの自信を提供できると思います。
Eonil 2016

2
これは単にこれまでで最高の説明です。
エドガーキルジャック2018年

2
@IceTea、質問に対する洞察/直感を提供します。n対してチャートアウトするとn/2、両方が直線になることがわかります。これは、成長率が似ているので、それらを同じクラスに配置します(チャートの形状と考えてください)。同様に、チャートを作成するlog_2log_3、どちらも「類似した形状」または「類似した成長率」をとっていることがわかります。
James Oravec

1
@ IceTea、@ Shaiと@Jamesによる説明の方が正確です。n/2 or 2n or n+2 or nグラフには異なる線が表示されますが、成長率は同じです。つまり、すべての成長率が線形成長に従います。
Naresh Joshi 2018

2
2つのネストされたループがあるが、2番目のイテレーターが最初のループに依存している場合、この依存関係は時間の複雑さに影響しますか?
Bionix1441

131

取る関数がある場合:

1 millisecond to complete if you have 2 elements.
2 milliseconds to complete if you have 4 elements.
3 milliseconds to complete if you have 8 elements.
4 milliseconds to complete if you have 16 elements.
...
n milliseconds to complete if you have 2^n elements.

次に、log 2(n)時間かかります。ランダウの記号、緩く言えば、関係だけ大きいnについて真である必要があり、一定の要因と小さい用語は無視することができることを意味します。


log2(n)はo(log n)と同じですか?
Sven van den Boogaart 2017年

はい、こちらの別の回答についてはnawfalのコメントを参照してください:(コピーと貼り付け)-プログラミングコンテキストでは、ログのベースは2(10ではなく)であるため、O(log n)は10要素の場合は1秒、20の場合は2秒のようにスケーリングされます、40 for 3など
Andrejs

@SvenvandenBoogaart、このソリューションの例log_2は、クラスに含まれるを示していO(log(n))ます。他の多くは、同じクラスであり、O(log(n))すなわちlog_xx > 1
ジェームズOravec

@Andrejs、あなたのコメントso O(log n) scales like 1 sec for 10 elements, 2 sec for 20, 3 for 40 etcは不正確です。そのパターン/クラスはO(n)not と一致/整列しO(log(n))ます。誰かに興味があった場合にはlog_1010個の要素、等2 100秒、1000 3、のための1秒となり、その後同等例
ジェームスOravec

99

対数実行時間(O(log n))は、本質的に、実行時間が入力サイズの対数に比例して増加することを意味します。たとえば、10アイテムが最大である程度の時間をx要し、100アイテムが最大で2x10,000アイテムを要する場合は最大で4x、それからO(log n)時間の複雑さのように見えます。


1
+1ですが、実際には、log10ではなくlog2であることを指摘してください。
Adriano Varoli Piazza

62
log2またはlog10は無関係です。それらはスケールファクタが異なるだけで、同じ順序になります。つまり、同じ速度で成長します。
Noldorin 2010

17
対数の面白い点は、相対的な高さを比較する場合、使用する正確な底は重要ではないということです。log 10,000 / log 100使用するベースに関係なく2です。
アノン。

12
細かく言えば、O(lg n)はランタイムが最大で lg nに比例することを意味します。あなたが説明するのはTheta(lg n)です。

1
@ rgrig:それは本当です。big-Oの上限の性質を示すために、いくつかの「多くても」編集しました。
アノン。

95

対数

では、実際に対数とは何かを十分に理解してみましょう。

ロープがあり、それを馬に結び付けたとします。ロープが馬に直接結ばれている場合、馬が(たとえば、男性から)引き離す必要がある力は直接1です。

ここで、ロープがポールの周りをループしていると想像してください。逃げる馬は何倍も強く引っ張らなければならないでしょう。回数はロープの粗さとポールのサイズに依存しますが、ロープが完全に回転したときに、強度に10を掛けると仮定します。

ロープが1回ループされると、馬は10倍強く引く必要があります。人間が馬を本当に難しくすることを決めた場合、彼はロープを再びポールに巻き付け、強度をさらに10倍上げることができます。3番目のループでは、強度がさらに10倍に増加します。

ここに画像の説明を入力してください

ループごとに、値が10ずつ増加していることがわかります。任意の数値を取得するために必要なターン数は、数値の対数と呼ばれます。 1,000,000。

3は1,000の対数、6は1,000,000(10を底とする)の対数です。

では、O(log n)は実際にはどういう意味ですか?

上記の例では、「成長率」はO(log n)です。追加のループごとに、ロープが処理できる力は10倍になります。

Turns | Max Force
  0   |   1
  1   |   10
  2   |   100
  3   |   1000
  4   |   10000
  n   |   10^n

さて、上記の例では基数10を使用していましたが、幸い、big o表記について話すとき、ログの基数は重要ではありません。

ここで、1〜100の数値を推測しようとしているとしましょう。

Your Friend: Guess my number between 1-100! 
Your Guess: 50
Your Friend: Lower!
Your Guess: 25
Your Friend: Lower!
Your Guess: 13
Your Friend: Higher!
Your Guess: 19
Your Friend: Higher!
Your Friend: 22
Your Guess: Lower!
Your Guess: 20
Your Friend: Higher!
Your Guess: 21
Your Friend: YOU GOT IT!  

これを正しく行うには、7回の推測が必要でした。しかし、ここでの関係は何ですか?追加の各推測から推測できるアイテムの最大量はいくつですか?

Guesses | Items
  1     |   2
  2     |   4
  3     |   8
  4     |   16
  5     |   32
  6     |   64
  7     |   128
  10    |   1024

グラフを使用すると、バイナリ検索を使用して1〜100の数値を推測すると、最大 7回の試行が必要になることがわかります。128の数値がある場合、7回の試行でその数を推測することもできますが、129の数値では最大 8回の試行が行われます(対数との関係で、128の値の範囲では7回の推測、1024の値の範囲では10回の推測が必要になります) 。7は128の対数、10は1024(底2)の対数です。

「せいぜい」太字になっていることに注意してください。Big-O表記は常に最悪のケースを指します。運が良ければ、1回の試行で数を推測できるので、最良のケースはO(1)ですが、それは別の話です。

すべての推測で、データセットが縮小していることがわかります。アルゴリズムに対数時間があるかどうかを識別するための良い目安は、データセットが各反復後に特定の順序で縮小するかどうかを確認することです。

O(n log n)はどうですか?

最終的には線形時間O(n log(n))アルゴリズムに出くわします。経験則では、上記再び適用されますが、この時間は、対数関数が実行する必要がありますn回はリストのサイズ縮小例えばn回マージソートのようなアルゴリズムで発生し、。

アルゴリズム時間がn log nであるかどうかは簡単に識別できます。リスト(O(n))を反復処理する外側のループを探します。次に、内部ループがあるかどうかを確認します。内側のループが各反復でデータセットをカット/削減している場合、そのループは(O(log n))なので、全体的なアルゴリズムは= O(n log n)です。

免責事項:ロープ対数の例は、W.Sawyerによる優れた数学者の喜びの本から取得されました


いいえ In our example above, our 'growth rate' is O(log n). For every additional loop, the force our rope can handle is 10 times more。n==ループ数とour 'growth rate'=> 10 ^ n を示すグラフでサポートされています。これはlog nではありません。例を作ることで正しくすることができますn=# horses、これは抑制するためにlog nループを必要とします。貧しい人々の例は、自分が理解していると信じているだけの学生を生み出します。
psimpson

56

時間はNの桁数に比例すると言うことで、O(log N)を直感的に考えることができます。

操作が入力の各桁またはビットに対して一定時間の作業を実行する場合、操作全体は、入力の大きさではなく、入力の桁またはビットの数に比例して時間がかかります。したがって、O(N)ではなくO(log N)になります。

操作が一連の一定時間の決定を行い、それぞれが考慮される入力のサイズを半分にする(3、4、5の因数で減少する)場合、全体は対数の底2(底3 、base 4、base 5 ...)O(N)ではなく、入力のサイズNです。

等々。


7
ほとんどの説明よりも十分に正確で、より簡単に理解できると思います。
T。

の説明log<sub>10</sub> Nですよね?
LiuYan刘研

1
@LiuYan刘研彼らは、桁数が何の基数であるかを言っていませんでした。しかし、いずれにしても、log₂(n)=log₁₀(n)/log₁₀(2)と1 /log₁₀(2)は定数乗数です。同じ原則が他のすべてのベースに適用されます。これは2つのことを示しています。まず、ムーンシャドウの原則は、ベースが何であっても適用されます(ベースが低いほど、推定の「ジャグ」は少なくなります)。また、結論に至った計算に関係なく、O(log n)はO(log n)です。 。
Jon Hanna

"比例" ... "それぞれが入力のサイズを半分にします" ??????
csguy

52

O(log n)で実行されるアルゴリズムを常に精神的に視覚化しなければならなかった最良の方法は、次のとおりです。

問題のサイズを乗法量で増やす(つまり、サイズに10を掛ける)と、作業量は加法量だけ増加します。

これをバイナリツリーの質問に適用すると、適切なアプリケーションが得られます。バイナリツリーのノード数を2倍にすると、高さは1だけ増加します(加算量)。もう一度2倍にしても、1だけ増加します(明らかに、バランスが保たれていると思います)。そうすれば、問題のサイズが倍になったときに作業を2倍にするのではなく、ごくわずかに多くの作業を行うだけです。それがO(log n)アルゴリズムが素晴らしい理由です。


52

まず、以下の本を読むことをお勧めします。

アルゴリズム(第4版)

次に、いくつかの機能とその予想される複雑さを示します。数字はステートメントの実行頻度を示しています

ここにいくつかの機能とそれらの予想される複雑さがあります

次のBig-O Complexityチャートbigocheatsheetから取得 Big-O複雑度チャート

最後に、非常にシンプルなショーケースで、計算方法を示します。

プログラムのステートメント実行頻度の分析。

プログラムの実行時間の分析(例)。

プログラムの実行時間を分析する


5
私はO(n log n)を悪いバスケットに入れません。それは公正なものに属しています。
アンドレ・Werlang

big-O複雑度チャート(上記)を表示するとき、O(n)はピンク/オレンジの境界ではなく、実際の線形ポイントであることを覚えておく必要があります。@Andreこれが、O(n log n)が「悪い」パフォーマンスブラケットで正しくマークされている理由です。線形よりパフォーマンスが悪いです。
JavaBeast 2018年

@JavaBeastは正しいですが、O(n log n)のパフォーマンスは技術的にO(n)よりも劣りますが、上記の表を参照してください。これらは、両者の優れた比較を示しています(2つの増加を参照)。異なるソースからのチャートでは、O(1)とO(log n)を同じ良い/優れたものにするため、矛盾しています。それらの相対的な成長順序は、O(n)およびO(n log n)に匹敵します。tl; dr; O(n log n)は優れていませんが、悪くはありません。
アンドレ・Werlang

1
この答えは間違っています!N = N * Nであると想定しています。実際、N = N!あなたの例は実際にはN乗です。グラフでも同じです。あなたのO(n)は、実際には恐ろしいものと悪いものの間の分かれ目になるはずです。数学的な証明:forループはO(1)で一定であると言います。これは、1が実際に意味することであり、Nに依存しません。これは、変数ではないことを意味します。しかし、それはNに依存しているため、変動します。Nの2倍、時間の半分です。したがって、それは無効です。それがその本からであるならば、それを買わないでください!あなたが示したコードグラフィックは実在のものではなく、冗談であり、「Theesome」です。これは、3人が同時にセックスをすることを意味します。OMG
jgmjgm

1
O(n)は対角線上にあるべきではありませんか?
gyosifov

46

ログb(n)とは?

これは、サイズ1のセクションに到達する前に、長さnの対数をbの等しい部分に繰り返しカットできる回数です。


素晴らしいコメント!それは簡潔で正確な答えです。
DennisL 2018

18

分割統治アルゴリズムには通常logn、実行時間の要素があります。これは、入力が半分になることを繰り返すことから生じます。

二分探索の場合、反復のたびに入力の半分が破棄されます。Big-O表記では、ログは2を底とするログであることに注意してください。

編集:前述のように、ログベースは重要ではありませんが、アルゴリズムのBig-Oパフォーマンスを導出する場合、ログ係数は半分になるため、ベース2と考えるのはなぜでしょうか。


2
なぜログベース2なのですか?たとえば、ランダム化されたクイックソートでは、底が2だとは思いません。私が知る限り、底は関係ありません。対数の底a(n)= log2(n)/ log2(a)なので、すべての対数定数は定数によって異なり、big-o表記では定数は無視されます。実際、あなたが定数を書いているので、ログのベースをbig-o表記で書くことは私の意見の誤りです。
IVlad


これは任意のベースに変換でき、問題ではないことは非常に当てはまりますが、Big-Oのパフォーマンスを引き出そうとしていて、常に半分になっている場合は、ログベース10がコードに反映されていないことを理解するのに役立ちます。
デビッドカナレク2010

余談:Bツリーなど、ノードのファンアウトが2より大きい(つまり、バイナリツリーよりも「広い」)場合、O(logn)の成長が見られます。 -征服、しかしログのベースはファンアウトに関連します。
Roger Lipscombe、

実際、log 2の余談はかなり役に立ちました。
Dan Rosenstark

15

しかし、正確にはO(log n)とは何ですか?たとえば、完全な二分木の高さがO(log n)であるとはどういう意味ですか?

これを「完全なバイナリツリーの高さはlog n」であると言い換えます。完全なバイナリツリーの高さを計算すると、O(log n)になります。

対数時間で関数を特定する方法がわかりません。

対数は基本的に指数の逆です。したがって、関数の各「ステップ」が元のアイテムセットから要素の要素を排除している場合、それは対数時間アルゴリズムです。

ツリーの例では、ノードのレベルを下げると、トラバースを続行すると指数関数的な数の要素が削減されることが簡単にわかります。名前でソートされた電話帳を参照する一般的な例は、基本的にバイナリ検索ツリーをたどることと同じです(中央のページがルート要素であり、左または右に進むかどうかを各ステップで推測できます)。


3
+1は、「対数は本質的にべき乗の逆数である」と述べています。
talonx 2013年

12

これら2つのケースにはO(log n)時間かかります

case 1: f(int n) {
      int i;
      for (i = 1; i < n; i=i*2)
        printf("%d", i);
    }


 case 2  : f(int n) {
      int i;
      for (i = n; i>=1 ; i=i/2)
        printf("%d", i);
    }

私は何かが欠けていると確信していますが、常に0であるとは限らず、ループは両方の場合に永久に実行されます。0* 2 = 0と0/2 = 0ですか?
dj_segfault 2013

2
@dj_segfault、それは私の間違いでした。今では理にかなっていると思います.. :)
Ravi Bisla

@RaviBisla他の回答では、10の入力は1回のループで最大10回かかり、100の入力は1回の入力時間の3倍かかります。これは、これらの例の場合とは明らかに異なります。stackoverflow.com/a/2307330/1667868
Sven van den Boogaart 2017年

12

O(log n)は少し誤解を招くようです。より正確には、O(log 2 n)、つまり(2を底とする対数)です。

平衡二分木の高さは、O(logです2すべてのノードは、2つの(ログのように「2」を注意しているので、n)を2 n)の子ノード。したがって、n個のノードを持つツリーの高さはlog 2 nです。

別の例はバイナリ検索で、実行時間はO(log 2 n)です。これは、各ステップで検索スペースを2で除算するためです。


4
O(log n)は、O(ld n)またはO(LN n)と同じ順序です。それらは比例しています。学習目的ではldを使用する方が簡単であることを理解しています。
ヘリオス2010

4
「より正確にはO(ld n)です」-いいえ、そうではありません。すべてのログは同じ順序です(それぞれが他のログと異なるのは、無視されるか無視できる一定の倍率のみです)。
ChrisW 2010

1
あなたは正しいクリス、非常に悪い言葉遣いです。ヘリオスが言ったようにそれを言うべきだった。学習/理解に役立ちますが、最終的にすべてのログは同じ順序になります。
stmax

10

O(log n) 対数に比例した時間で機能する関数(またはアルゴリズム、またはアルゴリズムのステップ)を指します(通常はほとんどの場合2を底としますが、常にそうとは限りません。いずれにしても、これはbig-O表記では意味がありません*)。入力のサイズの。

対数関数は、指数関数の逆関数です。別の言い方をすれば、入力が指数関数的に増加する場合(通常、それを直線的に考えるのではなく)、関数は線形的に増加します。

O(log n)実行時間は、あらゆる種類の分割統治アプリケーションで非常に一般的です。これは、(理想的には)作業を毎回半分にするためです。分割または征服の各ステップで、一定時間の作業(または一定時間ではないが、時間の増加がゆっくりである作業O(log n))を行っている場合、関数全体はになりO(log n)ます。代わりに、各ステップで入力の線形時間を必要とすることはかなり一般的です。これは、の合計時間の複雑さに相当しO(n log n)ます。

バイナリ検索の実行時の複雑さはの例ですO(log n)。これは、バイナリ検索では、配列を半分に分割し、各ステップで半分にのみ焦点を当てることにより、後の各ステップで入力の半分を常に無視しているためです。バイナリ検索では、考慮している配列の大きさに関わらず、次に行うことを理解するために、1つの要素をキーと比較するだけでよいので、各ステップは一定の時間です。したがって、およそlog(n)/ log(2)ステップを実行します。

マージソートの実行時の複雑さはの例ですO(n log n)。これは、各ステップで配列を半分に分割しているため、合計でおよそlog(n)/ log(2)ステップになるためです。ただし、各ステップですべての要素に対してマージ操作を実行する必要があります(n / 2要素の2つのサブリストに対する1つのマージ操作か、n / 4要素の4つのサブリストに対する2つのマージ操作かは関係ありません。各ステップでn個の要素に対してこれを行います)。したがって、全体の複雑さはO(n log n)です。

* big-O表記は、定義により、定数は関係ないことに注意してください。また、対数の基本ルール変更により、異なる底の対数間の唯一の違いは一定の要因です。


最後の*ノートは、対数が2または10に基づいているという私の混乱を解決しました:)どうもありがとうございました。
yahya


9

簡単に言うと、アルゴリズムの各ステップで、作業を半分に削減できます。(漸近的に3番目、4番目、...と同等)


2
この答えは非常に不正確です。まず、2を底とする対数の場合にのみ、作業を半分に削減することを考えることができます。この回答(および元の質問に対するほとんどの回答)が非常に多くの賛成票を受け取ったのは本当に驚くべきことです。「(3番目、4番目、…と漸近的に同等)」?時間がないのになぜ質問に答えるのですか?
nbro 2016

8

対数関数をグラフィカルな計算機などでプロットすると、立ち上がりが非常に遅いことがわかります。線形関数よりもさらにゆっくりです。

これが、対数時間の複雑さをもつアルゴリズムが非常に求められている理由です。本当に大きなn(たとえば、n = 10 ^ 8としましょう)でも、許容範囲を超えるパフォーマンスを発揮します。


7

しかし、正確にはO(log n)は何ですか

それが正確に意味することは、「にn向かう傾向としてinfinity、どこにtime向かう傾向が一定の倍率であるか」です。a*log(n)a

または実際には、それはそれがまったく意味しているわけではありません。たぶん、「でtime割るa*log(n)傾向」で割ったようなものを意味します1

「傾向」は「分析」からの通常の数学的な意味を持ちます。たとえば、「任意の小さいゼロ以外の定数を選択すると、より大きいすべての値よりも小さいようなk対応する値を見つけることができます。」X((time/(a*log(n))) - 1)knX


一言で言えば、それは時間の方程式が他のいくつかの要素を持っているかもしれないことを意味します。例えば、それは一定の起動時間を持っているかもしれません。しかし、これらの他のコンポーネントは、nの値が大きい場合には重要ではないように見え、a * log(n)はnが大きい場合の支配的な項です。

たとえば、方程式が次の場合...

time(n)= a + b log(n)+ c n + d n n

...そして、これはO(nの2乗)になります。これは、定数a、b、c、およびゼロ以外のdのd*n*n値に関係なく、nの十分に大きい値の場合、項は常に他の項よりも優先されるためです。

これがビットO表記の意味です。つまり、「十分に大きなnの主要な項の次数は何か」を意味します。



7

昔、コーメンなどの本で読んだ面白いことを追加できます。ここで、問題を想像してみましょう。問題空間で解決策を見つけなければなりません。この問題空間は有限でなければなりません。

ここで、アルゴリズムの反復ごとにこのスペースの一部を切り取ることを証明できる場合、それはある制限以上であり、これはアルゴリズムがO(logN)時間で実行されていることを意味します。

ここで、絶対的な制限ではなく、相対的な端数制限について話していることを指摘しておきます。二分探索は古典的な例です。各ステップで、問題の空間の1/2を捨てます。しかし、二分探索だけがそのような例ではありません。どういうわけか、各ステップで問題のスペースの少なくとも1/128を破棄することを証明したとします。つまり、プログラムはO(logN)時間で実行されていますが、バイナリ検索よりもかなり低速です。これは、再帰的アルゴリズムの分析に非常に役立つヒントです。多くの場合、各ステップで再帰がいくつかのバリアントを使用しないことを証明できます。これにより、問題の空間で一部がカットオフされます。


6

forループの例を挙げれば、コンセプトを理解すれば、さまざまなコンテキストで理解する方が簡単になるでしょう。

つまり、ループではステップが指数関数的に増加します。例えば

for (i=1; i<=n; i=i*2) {;}

このプログラムのO表記の複雑さはO(log(n))です。手でループしてみます(nは512〜1023(1024を除く)の間のどこかです)。

step: 1   2   3   4   5    6    7    8     9     10
   i: 1   2   4   8   16   32   64   128   256   512

nは512と1023の間のどこかですが、10回の反復しか行われません。これは、ループのステップが指数関数的に増加するため、終了に到達するまでに10回の反復しか必要としないためです。

xの対数(aの底)は、a ^ xの逆関数です。

対数は指数の逆であると言っているようなものです。

今度はそれをそのように見てみてください。指数関数が非常に速く成長すると、対数は(逆に)非常に遅くなります。

O(n)とO(log(n))の違いは非常に大きく、O(n)とO(a ^ n)の違いに似ています(aは定数です)。


6

実際、n個の要素のリストがあり、そのリストからバイナリツリーを作成する場合(分割統治アルゴリズムなど)、サイズ1のリスト(葉)に達するまで2で除算し続けます。

最初のステップでは、2で除算します。次に2つのリスト(2 ^ 1)があり、それぞれを2で除算するので、4つのリスト(2 ^ 2)があり、再び除算すると、8つのリスト(2 ^ 3 )など、リストのサイズが1になるまで続けます。

それはあなたに方程式を与えます:

n/(2^steps)=1 <=> n=2^steps <=> lg(n)=steps

(各辺のlgをとります。lgは対数の底2です)


2
一部のマルウェアが、ノードを離れる前に2レベルでxの長さの新しいリストを挿入するまで。その後、それは無限ループのようです...
フランシス・キュグラー2017

1
コメントがありませんでした。私の説明は間違っていますか?
Dinaiz 2017

1
私は架空の冗談を言っていました。私はそれによって何も意味していませんでした。
Francis Cugler 2017年

6

アルゴリズムやコードを書くたびに、その漸近的な複雑さを分析しようとします。時間の複雑さとは異なります

漸近的複雑度はアルゴリズムの実行時間の動作であり、時間の複雑度は実際の実行時間です。しかし、一部の人々はこれらの用語を同じ意味で使用しています。

時間の複雑さはさまざまなパラメータに依存するためです。
1.物理システム
2.プログラミング言語
3.コーディングスタイル
4.その他...

実際の実行時間は、分析には適していません。


コードが何であれ、入力は同じであるため、代わりにパラメーターとして入力サイズを使用します。 したがって、実行時間は入力サイズの関数です。

以下は線形時間アルゴリズムの例です


線形検索
入力要素がn個ある場合、配列内の要素を検索するには、最大でn回の比較が必要です。つまり、どのプログラミング言語を使用する場合でも、どのコーディングスタイルを使用する場合でも、どのシステムで実行するかが決まります。最悪のシナリオでは、n回の比較のみが必要です。実行時間は入力サイズに線形に比例します。

そして、それは単なる検索ではなく、作業(インクリメント、比較、またはその他の操作)が何であれ、入力サイズの関数です。

したがって、アルゴリズムがO(log n)であると言う場合、実行時間はlog×入力サイズnであることを意味します。

入力サイズが大きくなると、実行される作業(ここでは実行時間)が大きくなります(したがって比例)。

      n      Work
      2     1 units of work
      4     2 units of work
      8     3 units of work

入力サイズが大きくなるにつれて、実行される作業が増加し、どのマシンからも独立していることがわかります。作業単位の値を見つけようとすると、実際には上記で指定したパラメーターに依存します。システムとすべてに応じて変化します。


5

木

log x to base b = y の逆です b^y = x

深さがdでサイズがnのM-aryツリーがある場合、次のようになります。

  • ツリー全体をトラバースする〜O(M ^ d)= O(n)

  • ツリー内の単一のパスを歩く〜O(d)= O(log n to base M)


5

情報技術では、次のことを意味します。

  f(n)=O(g(n)) If there is suitable constant C and N0 independent on N, 
  such that
  for all N>N0  "C*g(n) > f(n) > 0" is true.

Antこの表記はほとんど数学から取られたようです。

この記事には引用があります: DE Knuth、 "BIG OMICRON AND BIG OMEGA AND BIG THETA"、1976

ここで説明した問題に基づいて、より良い代替案がすぐに見つかる場合を除き SIGACTのメンバー、およびコンピューターサイエンスと数学のジャーナルの編集者は、上記で定義した表記法を採用することをお勧めします。

今日は2016年ですが、今日でも使用しています。


数学的分析では、次のことを意味します。

  lim (f(n)/g(n))=Constant; where n goes to +infinity

しかし、数学的分析でさえ、この記号は「C * g(n)> f(n)> 0」の意味で使用されることがありました。

私が大学で知っているように、シンボルはドイツの数学者ランダウ(1877-1938)によって誘導されました


4

検索が次のようになるため、完全なバイナリの例はO(ln n)です。

1 2 3 4 5 6 7 8 9 10 11 12

4を検索すると、6、3、次に4の3ヒットが得られます。log212 = 3は、必要なヒット数に適切に相当します。


例をありがとう。これは、アルゴリズムが分割統治法で対数時間をどのように使用できるかを明確に示しています。
Abc

したがって、そのループがn / 2の場合、常にlog(n)になりますか?
Gil Beyruth、2016

3

直感に基づく答えを探している場合、2つの解釈を提示したいと思います。

  1. 非常に広い丘と非常に高い丘を想像してください。丘の頂上に到達するには、2つの方法があります。1つは頂上に到達する丘の周りをらせん状に進む専用の経路であり、もう1つは階段を提供するために切り抜かれた彫刻のような小さなテラスです。最初の方法が線形時間O(n)で到達している場合、2番目の方法はO(log n)です。

  2. n入力として整数を受け入れ、nそれに比例して時間内に完了するアルゴリズムを想像してください。それがO(n)またはtheta(n)である場合、それが時間に比例して実行される場合number of digits or the number of bits in the binary representation on number、アルゴリズムはO(log n)またはthetaで実行されます。 (log n)時間。


編集してください。両方のシナリオで「O(n)またはtheta(n)」があります...?また、これはサイズと#桁の比較です。n = 10000000の場合はサイズ=== 128、n = 10000000の場合は数字=== 8と言っていますか?解明してください。
コーディ

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