Big O Notationの実行時間と償却時間について学習しています。 O(n)線形時間の概念を理解しています。これは、入力のサイズがアルゴリズムの成長に比例して影響することを意味しています...たとえば、二次時間O(n2)など。O(n!)回の順列ジェネレータなどのアルゴリズムでさえ、階乗。
たとえば、次の関数はO(n)です。これは、アルゴリズムがその入力n:
f(int n) {
int i;
for (i = 0; i < n; ++i)
printf("%d", i);
}
同様に、ネストされたループがある場合、時間はO(n2)。
しかし、O(log n)とは正確には何ですか?たとえば、完全な二分木の高さがO(log n)であるとはどういう意味ですか?
Logarithmが何であるかを(詳細にではないかもしれませんが)知っています。10 100 = 2ですが、対数時間で関数を識別する方法を理解できません。
ログ時間で機能を識別する方法がわかりません。
対数実行時関数の最も一般的な属性は次のとおりです。
または
たとえば、電話帳で人を検索するとO(log n)になるのはそのためです。正しい電話帳を見つけるために電話帳のevery人すべてをチェックする必要はありません。代わりに、名前がアルファベット順に並んでいる場所に基づいて検索して分割統治することができます。また、各セクションでは、最終的に誰かの電話番号を見つける前に各セクションのサブセットを調べるだけで済みます。
もちろん、電話帳を大きくしても長い時間はかかりますが、それに比例してサイズが増えても、それほど早くは成長しません。
電話帳の例を拡張して、他の種類の操作との実行時間を比較できます。私たちの電話帳には、一意の名前を持つbusiness( "イエローページ")と一意の名前を持たないpeople( "ホワイトページ")があるとします。電話番号は最大で1人の個人または会社に割り当てられます。また、特定のページにめくるには一定の時間がかかると仮定します。
以下は、ベストからワーストまで、電話帳に対して実行する操作の実行時間です。
O(1)(ベストケース): 会社名が掲載されているページと会社名から電話番号を探します。
O(1)(平均的な場合): ある人の名前が載っているページとその名前から、電話番号を見つけます。
O(log n): 人の名前を指定して、まだ検索していない本の部分の途中からランダムにポイントを選び、その人の名前がその時点で。それからその人の名前がある本の部分の中ほどでプロセスを繰り返す。 (これは人の名前の二分検索です。)
O(n): 電話番号に数字の "5"が含まれるすべての人を検索します。
O(n): 電話番号を指定して、その番号を持つ人または会社を見つけます。
O(n log n): プリンターのオフィスで混乱があり、電話帳にすべてのページがランダムな順序で挿入されていました。各ページの名前を見て、そのページを新しい空の電話帳の適切な場所に配置して、順序が正しくなるように修正します。
以下の例では、現在プリンタのオフィスにいます。電話帳は各居住者または会社に郵送されるのを待っています、そしてそれがどこに郵送されるべきであるかを特定するステッカーが各電話帳にあります。すべての人または企業が1つの電話帳を受け取ります。
O(n log n): 電話帳をパーソナライズしたいので、指定されたコピーで各人または会社の名前を見つけてから、本の中で自分の名前を丸で囲み、短い感謝を書きます。 - あなたは彼らの後援をお願いします。
O(n2): オフィスでミスが発生し、電話帳の各エントリの電話番号の末尾に余分な "0"が追加されています。いくらかのホワイトアウトを取り、各ゼロを取り除きます。
O(n・n!): 電話帳を出荷用ドックに積み込む準備ができました。残念ながら、本をロードすることになっていたロボットが混乱しています。それは、本をトラックにランダムな順序で入れていることです。さらに悪いことに、それはすべての本をトラックに積んでから、それらが正しい順番になっているかどうかをチェックし、そうでなければ、荷を降ろして最初からやり直します。 (これは恐ろしいボゴーソートです。)
O(nn): ロボットが物を正しくロードするようにロボットを固定します。翌日、同僚の1人があなたにいたずらをして、ローディングドックロボットを自動印刷システムに接続します。ロボットがオリジナルの本を読み込むたびに、ファクトリープリンターはすべての電話帳を重複して実行します。幸いなことに、ロボットのバグ検出システムは非常に洗練されているため、ロードするために本が重複している場合でも、それ以上印刷することはできません。
さらに数学的な説明をするために、ここで時間の複雑さがどのようにしてlog n
に到達するかを調べることができます。 https://hackernoon.com/what-does-the-time-complexity-o-log-n-actually-mean-45f94bb5bfbf
この質問にはすでにたくさんの良い答えが投稿されていますが、私たちは本当に重要なもの、すなわち図解された答えを見逃していると思います。
完全な二分木の高さがO(log n)であると言うことはどういう意味ですか?
次の図は二分木を示しています。各レベルが上のレベルに比べて2倍の数のノードを含むことに注意してください(したがって、 binary )。
二分検索は複雑さのある例ですO(log n)
。図1のツリーの最下位レベルにあるノードが、ソートされたコレクション内の項目を表すとしましょう。二分探索は分割統治法のアルゴリズムであり、この図は、この16項目のデータセットで検索しているレコードを見つけるために(最大でも)4回の比較が必要になる方法を示しています。
代わりに32個の要素を持つデータセットがあるとします。上の図を続けて、検索しているものを見つけるために5つの比較が必要になることを確認します。データ量を増やしたときにツリーが1レベルだけ深くなったからです。結果として、アルゴリズムの複雑さは対数次数として記述することができます。
普通の一枚の紙にlog(n)
をプロットすると、n
が増加するにつれて曲線の上昇が減速するグラフになります。
O(log N)
は基本的にn
が指数関数的に上がる一方で時間が直線的に上がることを意味します。したがって、1
要素の計算に10
秒かかる場合、2
要素の計算に100
秒、3
要素の計算に1000
秒というようになります。
二分探索など、アルゴリズムを分割して征服するのはO(log n)
です。もう1つの例は、配列を2つの部分に分割するたびにピボット要素を見つけるのにO(N)
時間がかかるクイックソートです。それゆえN O(log N)
概要
他の人たちは、ツリー図のような良い図の例を挙げています。簡単なコード例は見たことがありません。それで、私の説明に加えて、私は異なるアルゴリズムカテゴリーの複雑さを説明するために簡単なprintステートメントでいくつかのアルゴリズムを提供するつもりです。
まず、Logarithmの一般的な概念を知りたいと思うでしょう。これは https://en.wikipedia.org/wiki/Logarithm から入手できます。自然科学はe
と自然対数を使います。コンピュータはバイナリベースであるため、工学弟子はlog_10(log base 10)を使用し、コンピュータ科学者はlog_2(log base 2)を頻繁に使用します。自然対数の省略形がln()
と表示されることもありますが、エンジニアは通常_10を省略し、単にlog()
を使用し、log_2はlg()
と省略形にします。すべての種類の対数は同じように成長するため、同じ種類のlog(n)
を共有しています。
以下のコード例を見ると、O(1)、次にO(n)、次にO(n ^ 2)の順に見ることをお勧めします。あなたがそれらに慣れたら、それから他の人たちを見てください。微妙な変更でも同じ分類になる可能性があることを示すために、わかりやすい例とバリエーションを含めました。
O(1)、O(n)、O(logn)などを成長のクラスまたはカテゴリと考えることができます。いくつかのカテゴリーは他よりも時間がかかります。これらのカテゴリーは、アルゴリズムのパフォーマンスを順序付ける方法を私たちに与えるのに役立ちます。入力nが大きくなるにつれて速くなるものもあります。次の表は、上記の成長を数値的に示しています。以下の表では、log(n)をlog_2の上限として考えます。
さまざまなBig Oカテゴリの簡単なコード例:
O(1) - 一定時間の例:
アルゴリズム1は、helloを一度出力し、それはnには依存しないので、常に一定時間で実行されるので、O(1)
です。
print "hello";
アルゴリズム2はhelloを3回出力しますが、入力サイズには依存しません。 nが大きくなっても、このアルゴリズムは常にhelloを3回だけ出力します。 3ということは定数なので、このアルゴリズムもO(1)
です。
print "hello";
print "hello";
print "hello";
O(log(n)) - 対数の例:
アルゴリズム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を示しています。 i
は1から3から9から27になります。
for(int i = 1; i <= n; i = i * 3)
print "hello";
アルゴリズム5は、数値が1より大きく、結果がそれ自身に対して繰り返し乗算される限り、対数アルゴリズムを見ていることを示すのに役立つので重要です。
for(double i = 1; i < n; i = i * 1.02)
print "hello";
O(n) - 線形時間の例:
このアルゴリズムは単純で、helloをn回出力します。
for(int i = 0; i < n; i++)
print "hello";
このアルゴリズムは、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)例:
これを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はアルゴリズム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乗の例:
O(n^2)
はループの標準を入れ子にすることで簡単に得られます。
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
print "hello";
アルゴリズム10と同じですが、いくつかのバリエーションがあります。
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j = j + 2)
print "hello";
O(n ^ 3) - 立方体の例:
これはアルゴリズム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";
アルゴリズム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";
要約
上記は、いくつかの簡単な例と、実際には分析を変更しない、わずかな変更を加えることができることを示すのに役立つバリエーションを示しています。うまくいけば、それはあなたに十分な洞察を与えます。
あなたが取る関数を持っていたならば:
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.
それからログを取ります2(n)時間です。 Big O記法 は、大まかに言って、関係が大きいnに対してのみ成り立つ必要があることを意味しており、定数と小さい項は無視できるということです。
対数実行時間(O(log n)
)は、基本的に、実行時間が入力サイズの 対数 に比例して増加することを意味します。たとえば、10項目が最大である程度の時間をかけ、x
が100個を上限とします。たとえば、2x
と10,000個のアイテムが最大で4x
を要する場合、それはO(log n)
時間の複雑さのように見えます。
対数
それでは、実際に対数が何であるかを十分に理解してみましょう。
ロープを持っていて、それを馬に縛ったと想像してください。ロープが直接馬に縛られている場合、馬が引き離す必要がある力(たとえば、人間から)は直接1です。
今度はロープが棒のまわりでループしていると想像してください。逃げる馬は今では何度も強く引っ張る必要があります。時間はロープの荒さとポールのサイズによって異なりますが、ロープの強度が10倍になるとしましょう(ロープが完全に曲がるとき)。
ロープを一度ループすると、馬は10倍強く引く必要があります。人間が馬にとってそれを本当に困難にすることを決心した場合、彼はロープを再びポールに巻きつけて、その強度をさらに10倍増加させるかもしれません。 3回目のループでも強度はさらに10倍増加します。
ループごとに、値が10ずつ増加することがわかります。任意の数を取得するのに必要なターン数は、数の対数と呼ばれます。つまり、強度の3倍の1000倍の強度が必要です。 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を底としていましたが、幸いなことに私たちが大表記について話すとき、ログの底は重要ではありません。
今、あなたが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個の推測が必要です)。対数1024(2を底とする)).
私は「せいぜい」太字にしていることに注意してください。大表記は、常に最悪の場合を表します。運がよければ、1回の試行で数値を推測できるので、最適なケースはO(1)ですが、それは別の話です。
データセットが縮小していることには、推測があることがわかります。アルゴリズムに対数時間があるかどうかを識別するための優れた経験則は、各反復後にデータセットが一定の順序で縮小するかどうかを確認することです。
O(n log n)はどうですか?
最終的には対数時間O(n log(n)algorithm)に遭遇するでしょうが、この場合も対数関数はn回実行する必要があります。例えば、リストのサイズを小さくするn times、マージソートのようなアルゴリズムで発生します。
アルゴリズム時間がn log nかどうかを簡単に識別できます。リスト(O(n))を反復処理する外側のループを探します。次に、内側のループがあるかどうかを確認してください。内側のループがcut/reduction各反復のデータセット)の場合、そのループは(O(log n)であるため、全体のアルゴリズムは= O(n log n)です。
免責事項:ロープ対数の例はW.Sawyerによる優れた Mathematician's Delightの本から得られた 。
時間はNの桁数に比例すると言うことで、O(log N)を直感的に考えることができます。
操作が入力の各桁またはビットに対して一定時間の作業を実行する場合、操作全体は入力の大きさではなく、入力内の桁またはビットの数に比例した時間がかかります。したがって、O(N)ではなくO(log N)です。
ある操作が、考慮される入力のサイズを半分にする(3、4、5 ...の係数で減らす)一連の一定時間決定を行う場合、全体として対数2(比例3)に比例した時間がかかります。 、(4)、(base 5、...)を入力のサイズNとします。O(N)ではなく。
等々。
O(log n)で実行されるアルゴリズムを精神的に視覚化しなければならなかった最善の方法は次のとおりです。
問題のサイズを乗法的な量だけ増やす(つまり、サイズを10倍する)と、作業量は加算的な量だけ増えます。
これをあなたの二分木問題に当てはめると良い応用ができます:あなたが二分木の中のノードの数を二倍にした場合、高さは1だけ増加します(追加量)。あなたが再びそれを倍増するならば、それはまだ1だけ増加しました(明らかに私はそれがバランスがとれているようにとどまると仮定します)。そうすれば、問題のサイズが倍増したときに作業を2倍にするのではなく、ほんの少しだけ作業を増やすことになります。それが、O(log n)アルゴリズムが素晴らしい理由です。
ログとはb(n)
サイズ1のセクションに達する前に、長さnのログをb個の等しい部分に繰り返しカットすることができる回数です。
まず私はあなたに次の本を読むことを勧めます。
ここにいくつかの機能とその予想される複雑さがあります。数字は ステートメントの実行頻度 を示しています。
次の Big-O複雑度チャート これも bigocheatsheet
最後の非常に単純なショーケースは、それがどのように計算されるかを示しています。
プログラムのステートメント実行頻度の構造.
プログラムの実行時間を分析する(例)。
分割統治アルゴリズムには通常、実行時間に対するlogn
コンポーネントがあります。これは入力が半分ずつ繰り返されることに由来します。
二分検索の場合は、繰り返しのたびに入力の半分が捨てられます。 Big-O表記法では、logは2を底とする対数です。
編集:前述したように、対数ベースは重要ではありませんが、アルゴリズムのBig-Oパフォーマンスを導き出すとき、対数係数は半分になることから来るので、なぜ私はそれを基数2と考えます。
しかし、O(log n)とは一体何でしょうか。例えば、完全な二分木の高さがO(log n)であると言うことはどういう意味ですか?
これを「完全な二分木の高さはlog n」と言い換えます。完全な二分木の高さを計算すると、O(log n)になります。
対数時間でどのように関数を識別するか理解できません。
対数は基本的に指数の逆数です。そのため、関数の各「ステップ」が元の項目セットから要素のfactorを削除している場合、それは対数時間アルゴリズムです。
ツリーの例では、ノードのレベルを下げると、移動を続けるにつれて指数関数的な数の要素が削減されることが簡単にわかります。名前順に分類された電話帳を調べる一般的な例は、基本的にバイナリ検索ツリーをたどるのと同じです(中央のページがルート要素であり、各ステップで左右どちらに進むかを推測できます)。
これら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);
}
O(log n)
は、対数に比例した時間で機能する関数(またはアルゴリズム、またはアルゴリズム内のステップ)を表します(通常は2を底としますが、常にというわけではありません)。入力のサイズの)。
対数関数は指数関数の逆関数です。別の言い方をすれば、入力が指数関数的に(通常考えているように線形ではなく)増加すると、関数は線形に増加します。
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表記、 は定義により 、定数は関係ありません。また、対数に対する 基底規則の変更 によって、異なる基底の対数間の唯一の違いは定数係数です。
O(log n)は少し誤解を招く可能性があります。より正確にはO(log n)です。2 n)、すなわち(2を底とする対数)。
平衡二分木の高さはO(log2 n)すべてのノードに2つあります(ログのように「two」に注意してください)。2 n)子ノードしたがって、n個のノードを持つツリーはlogの高さを持ちます。2 n。
もう1つの例はバイナリサーチで、実行時間はOです(log2 n)すべてのステップでサーチスペースを2で割るので。
それは単にこのタスクに必要な時間がlog(n)で増加することを意味します(例:n = 10の場合は2秒、n = 100の場合は4秒、...)。 バイナリ検索アルゴリズム および Big O表記法 に関するウィキペディアの記事を読んでください。
簡単に言うと、アルゴリズムの各ステップで、作業を半分にすることができます。 (漸近的に3番目、4番目、...と等価)
対数関数をグラフィカル電卓などにプロットすると、実際にはゆっくりと上がるのがわかります。線形関数よりもさらに遅くなります。
対数時間の複雑さを持つアルゴリズムが非常に求められている理由はここにあります:たとえ本当に大きいn(例えば、n = 10 ^ 8としても)のために、彼らは容認できる以上に実行します。
しかし、まさにO(log n)
それが正確に意味するのは、「n
はinfinity
に向かう傾向があるので、time
はa*log(n)
に向かう傾向があるので、a
は一定のスケーリング係数である」ということです。
または実際には、それはそれほど意味がありません。おそらく「time
/a*log(n)
」は「1
」になる傾向があります。
「〜に向く」は「分析」からの通常の数学的意味を持ちます。例えば、「 any 任意に小さい0以外の定数k
を選ぶと、((time/(a*log(n))) - 1)
がX
より小さくなるような対応する値k
を見つけることができます。 n
のすべての値がX
より大きい場合
簡単に言えば、それは時間の方程式が他のいくつかの要素を持つかもしれないことを意味します。起動時間は一定です。しかし、これらの他の要素はnの大きな値に対しては無意味になりやすく、a * log(n)は大きなnに対する支配的な項です。
たとえば、方程式が.
時間(n)= a + b log(n)+ c n + d n n
なぜなら、定数a、b、c、およびゼロでないdの値がどうであっても、d*n*n
の項が常に十分に大きいnの値に対して他の項を支配するからです。 。
それがO表記法が意味するものです:それは「どんな十分に大きいnのための支配的な用語の次数であるか」を意味します。
私は久しぶりに私がKormen等による本で読んだ何か面白いものを加えることができます。さて、問題空間を解く必要がある問題を想像してください。この問題空間は有限であるべきです。
さて、あなたが証明することができれば、あなたのアルゴリズムの反復のたびに、あなたがこのスペースの一部を切り捨てること、それはある程度の限界を超えないこと、これはあなたのアルゴリズムがO(logN) timeで走っていることを意味する。
ここでは、絶対的な制限ではなく、相対的な端数の制限について説明していることを指摘しておく必要があります。二分探索は古典的な例です。各ステップで、問題空間の1/2を捨てます。しかし、そのような例はバイナリ検索だけではありません。あなたがどういうわけか証明したとしましょう、各ステップであなたは問題空間の少なくとも1/128を捨てると。つまり、プログラムはO(logN)の時点でまだ実行されていますが、バイナリ検索よりかなり遅くなります。これは再帰的アルゴリズムの分析において非常に良いヒントです。各ステップで再帰がいくつかの変種を使用しないことを証明できることが多く、これは問題空間の一部を切り捨てることにつながります。
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)の違いに似ています。定数)。
情報技術では、次のことを意味します。
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.
アリこの表記はほとんど数学から取ったようです。
この記事には引用があります: D.E。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)によって導入されました。
log x to base b = y
はb^y = x
の逆です
深さがdでサイズがnのM値木がある場合は、次のようになります。
ツリー全体をたどる〜O(M ^ d)= O(n)
ツリー内の単一のパスを歩く〜O(d) = O(ベースMにlog n)
実際には、n個の要素のリストがあり、そのリストから二分木を作成する場合(分割統治法のアルゴリズムのように)、サイズ1のリスト(リーフ)に達するまで2で除算し続けます。
最初のステップでは、2で除算します。それから、2つのリスト(2 ^ 1)があり、それぞれを2で除算します。したがって、4つのリスト(2 ^ 2)があります。リストサイズが1になるまで)
それはあなたに方程式を与える:
n/(2^steps)=1 <=> n=2^steps <=> lg(n)=steps
(あなたは各辺のlgを取り、lgは対数ベース2です)
アルゴリズムやコードを書くたびに、その漸近的複雑性を分析しようとします。それはそれとは異なります 時間の複雑さ。
漸近計算量はアルゴリズムの実行時間の振る舞いであり、時間計算量は実際の実行時間です。しかし、一部の人々はこれらの用語を同じ意味で使っています。
時間の複雑さはさまざまなパラメータに依存するからです。
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
入力サイズが大きくなるにつれて、行われる作業が増え、どのマシンからも独立したものになります。そして、あなたが仕事の単位の値を見つけようとするならば、それは実際には上で指定されたパラメータに依存しています。
あなたが直感に基づく答えを探しているならば、私はあなたのために2つの解釈を上げたいと思います。
非常に広い基盤と同様に非常に高い丘を想像してください。丘の頂上に到達するには、2つの方法があります。1つは、頂上に達する丘の周りをらせん状に走る専用の道です。もう1つは、階段のように切り取られた彫刻のような小さなテラスです。最初の方法が線形時間O(n)で到達している場合、2番目の方法はO(log n)です。
整数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(logn)です。ここでの一例は、あなた自身のべき関数を計算する、
int power(int x, unsigned int y)
{
int temp;
if( y == 0)
return 1;
temp = power(x, y/2);
if (y%2 == 0)
return temp*temp;
else
return x*temp*temp;
}
からhttp://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/ /
完全なバイナリの例はO(ln n)です。検索は次のようになるからです。
1 2 3 4 5 6 7 8 9 10 11 12
4を検索すると、6、3、そして4の3つのヒットが得られます。そしてlog2 12 = 3で、これは必要な場合のヒット数の概算です。
私は、木の高さが根から葉までの最長経路の長さであり、ノードの高さがその節から葉までの最長経路の長さであることを付け加えたい。パスとは、2つのノード間でツリーを移動している間に遭遇するノードの数を意味します。 O(log n)の時間複雑度を達成するためには、木のバランスをとる必要があります。つまり、任意のノードの子の高さの差は1以下にする必要があります。バランスが取れていない限り、O(log n)。実際には、最悪のシナリオでは、ツリー内を検索する時間の複雑さはO(n)になります。
AVL tree
のようなバランスツリーを見てください。これは、ツリー内を検索しながら(log n)の時間の複雑さを保つためにデータを挿入しながらツリーのバランスをとることに作用します。
O(logn)は任意のコードの実行時性能を測定するための多項式時間計算量の1つです。
私はあなたが既に二分探索法について聞いたことがあることを願っています。
サイズNの配列で要素を見つける必要があるとしましょう。
基本的に、コードの実行はN N/2 N/4 N/8 ....のようなものです。
各レベルで行われたすべての作業を合計すると、n(1 + 1/2 + 1/4 ....)になり、それはO(logn)に等しくなります。