アルゴリズムの時間の複雑さを見つける方法


890

質問

アルゴリズムの時間の複雑さを見つける方法は?

SOに質問を投稿する前に何をしましたか?

私はこれこれと他の多くのリンクを通過しました

しかし、時間の複雑さを計算する方法についての明確でわかりやすい説明を見つけることができた場所はありませんでした。

私は何を知っていますか?

以下のコードのように単純なコードを言ってください:

char h = 'y'; // This will be executed 1 time
int abc = 0; // This will be executed 1 time

以下のようなループがあるとします。

for (int i = 0; i < N; i++) {        
    Console.Write('Hello World !');
}

int i = 0; これは一度だけ実行されます。時間は実際にi=0は宣言ではなく計算されます。

i <N; これはN + 1回実行されます

i ++; これはN回実行されます

したがって、このループに必要な操作の数は

{1+(N + 1)+ N} = 2N + 2

注:時間の複雑さの計算に関する私の理解に自信がないので、これはまだ間違っている可能性があります

知りたいことは?

わかりましたので、これらの小さな基本的な計算は知っていると思いますが、ほとんどの場合、時間の複雑さは

O(N)、O(n2)、O(log n)、O(n!) ....および他の多く

誰かがアルゴリズムの時間の複雑さを計算する方法を理解するのを手伝ってくれる?私のようにこれを知りたい初心者はたくさんいると思います。


138
興味のある方へのボーナス:Big Oチートシートbigocheatsheet.com
msanford

4
このブログをチェックしてください:mohalgorithmsorbit.blogspot.com。再帰アルゴリズムと(特に)反復アルゴリズムの両方をカバーしています。
Mohamed Ennahdi El Idrissi 2015

1
なぜConsole.Write( 'Hello World!'); 機械語命令ではありませんか?
Chetan 2017


1
@Chetan Console.Write複雑さを計算するときに考慮すべきことを意味している場合、それは事実ですが、この場合も多少関係がありません。これは、big-Oが無視する定数係数のみを変更するため(回答を参照)、最終結果はまだですO(N)の複雑さ。
Bernhard Barker 2017

回答:


394

アルゴリズムの時間の複雑さを見つける方法

入力のサイズの関数として実行する機械語命令の数を合計し、式を最大(Nが非常に大きい場合)の項に簡略化し、任意の簡略化定数係数を含めることができます。

たとえば、2N + 2機械命令を単純化してこれを単にと説明する方法を見てみましょうO(N)

なぜ2つ2のを削除するのですか?

Nが大きくなるときのアルゴリズムのパフォーマンスに関心があります。

2Nと2の2つの項を考えます。

Nが大きくなるとき、これら2つの項の相対的な影響は何ですか?Nが100万であるとします。

次に、最初の用語は200万で、2番目の用語は2のみです。

このため、大きなNの最大項を除くすべてを削除します。

これで、からに移動2N + 2しました2N

伝統的に、私たちは一定の要因までのパフォーマンスにのみ関心があります。

これは、Nが大きい場合に、パフォーマンスに一定の倍数の差があるかどうかを実際には気にしないことを意味します。そもそも2Nの単位は明確ではありません。したがって、定数係数で乗算または除算して、最も単純な式を得ることができます。

だから2NちょうどになりますN


53
「なぜO(2N + 2)がO(N)なのか」を非常にうまく説明してくれてありがとうございます。これはより大きな質問の一部にすぎません。隠されたリソースへのリンクまたは一般的に、O(N)、O(n2)、O(log n)、O(n!)などの時間の複雑さを解消する方法を知りたかったのです。それでも私は試すことができます:{)
Yasser Shaikh

3
かっこ内の複雑さは、アルゴリズムにかかる時間であり、説明した方法を使用して簡略化されています。アルゴリズムが実行する機械語命令の数を単純に合計することにより、アルゴリズムにかかる時間を計算します。私が説明したように、最も忙しいループのみを見て定数要素で除算することにより、単純化することができます。
Andrew Tomazos 2012年

4
回答例を提供することは、将来の読者にとって非常に役立ちます。サインアップする必要のあるリンクを渡すだけで、適切に説明されたテキストを読みたいだけのときは本当に役に立ちません。
bad_keypoints 2016年

2
DSと時間の複雑さについて十分な知識を得たい場合は、Dr。Naveen Garg(IIT Delhi Prof.)のビデオをご覧になることをお勧めします。リンクを確認してください。nptel.ac.in/courses/106102064
Rohit Bandil、2016年

2
(続き)この階層の高さはlog Nのオーダーになります。O(N!)と同様に、私のアナロジーはそれを削減する可能性は低いですが、順列はそのオーダーにあります-それは多項式または指数。ちょうど10個あります!6週間で数秒ですが、宇宙は20未満です!秒前。
John P

389

これは優れた記事です:http : //www.daniweb.com/software-development/computer-science/threads/13488/time-complexity-of-algorithm

以下の答えは上からコピーされます(優れたリンクが破産した場合)

時間の複雑さを計算するための最も一般的なメトリックは、Big O表記です。これにより、すべての定数係数が削除されるため、Nが無限に近づくときに、Nに関連して実行時間を推定できます。一般的には、次のように考えることができます。

statement;

一定です。ステートメントの実行時間は、Nに関連して変化しません。

for ( i = 0; i < N; i++ )
     statement;

線形です。ループの実行時間はNに正比例します。Nが2倍になると、実行時間も2倍になります。

for ( i = 0; i < N; i++ ) {
  for ( j = 0; j < N; j++ )
    statement;
}

二次式です。2つのループの実行時間はNの2乗に比例します。Nが2倍になると、実行時間はN * N増加します。

while ( low <= high ) {
  mid = ( low + high ) / 2;
  if ( target < list[mid] )
    high = mid - 1;
  else if ( target > list[mid] )
    low = mid + 1;
  else break;
}

対数です。アルゴリズムの実行時間は、Nを2で除算できる回数に比例します。これは、アルゴリズムが各反復で作業領域を半分に分割するためです。

void quicksort ( int list[], int left, int right )
{
  int pivot = partition ( list, left, right );
  quicksort ( list, left, pivot - 1 );
  quicksort ( list, pivot + 1, right );
}

N *ログ(N)です。実行時間は対数であるNループ(反復または再帰)で構成されているため、アルゴリズムは線形と対数の組み合わせです。

一般に、1次元のすべてのアイテムで何かを行うことは線形であり、2次元のすべてのアイテムで何かを行うことは2次であり、作業領域を半分に分割することは対数です。立方、指数、平方根などの他のBig Oメジャーもありますが、それほど一般的ではありません。ビッグO表記は、メジャーO ( <type> )がどこにあるか<type>として記述されます。クイックソートアルゴリズムはと記述されO ( N * log ( N ) )ます。

これはどれも、最良、平均、および最悪のケースの測定値を考慮していないことに注意してください。それぞれに独自のBig O表記があります。また、これは非常に単純化した説明です。Big Oが最も一般的ですが、これまでに示したよりも複雑です。ビッグオメガ、リトルオー、ビッグシータなどの他の表記法もあります。おそらく、アルゴリズム分析コース以外ではこれらに遭遇しないでしょう。;)


10
最悪の場合のクイックソートアルゴリズムの実行時間はN ^ 2ですが、この動作はまれです。
nbro 2015年

2
IIRC、リトルoおよびビッグオメガは、最高および平均のケースの複雑さ(ビッグOが最悪のケース)に使用されるため、「最高、平均、および最悪のケースのメジャー。それぞれに独自のビッグO表記があります。」間違いでしょう。さらに具体的な意味を持つ記号がさらにあり、CSは常に最も適切な記号を使用しているわけではありません。私はこれらすべてをランダウ記号という名前で学ぶようになりました。+1とにかくb / cベストアンサー。
hiergiltdiestfu

@hiergiltdiestfu Big-O、Big-Omegaなどは、アルゴリズムの実行時間の最高、平均、または最悪の場合に適用できます。OとΩは、最悪の場合と最良の場合の関係を教えてください。
Bernhard Barker 2017

また、いずれかの方法でビッグOを計算する方法を誰かが探している場合:stackoverflow.com/a/60354355/4260691
OhadM

最高の説明の1つ。
Shivaraj Patil

172

ここから抜粋- アルゴリズムの時間の複雑さの紹介

1.はじめに

コンピュータサイエンスでは、アルゴリズムの時間の複雑さは、入力を表す文字列の長さの関数として実行するためにアルゴリズムが要する時間を定量化します。

2. Big O表記

アルゴリズムの時間の複雑さは、通常、係数と低次の項を除外するビッグO表記を使用して表現されます。このように表現すると、時間の複雑さは漸近的に、つまり入力サイズが無限大になると説明されます。

たとえば、サイズnのすべての入力でアルゴリズムが必要とする時間が最大で5n 3 + 3nである場合、漸近時間の複雑度はO(n 3)です。詳細は後ほど。

いくつかの例:

  • 1 = O(n)
  • n = O(n 2
  • log(n)= O(n)
  • 2 n + 1 = O(n)

3. O(1)一定時間:

アルゴリズムは、入力サイズに関係なく同じ時間を必要とする場合、一定の時間で実行すると言われています。

例:

  • 配列:任意の要素にアクセスする
  • 固定サイズのスタック:pushおよびpopメソッド
  • 固定サイズのキュー:エンキューおよびデキューのメソッド

4. O(n)線形時間

アルゴリズムの実行時間が入力サイズに正比例する場合、アルゴリズムは線形時間で実行すると言われます。

以下の例を検討してください。以下では、要素を直線的に検索していますが、これはO(n)の時間複雑さを持っています。

int find = 66;
var numbers = new int[] { 33, 435, 36, 37, 43, 45, 66, 656, 2232 };
for (int i = 0; i < numbers.Length - 1; i++)
{
    if(find == numbers[i])
    {
        return;
    }
}

その他の例:

  • 配列:線形検索、走査、最小値の検索など
  • ArrayList:containsメソッド
  • キュー:containsメソッド

5. O(log n)対数時間:

アルゴリズムの実行時間が入力サイズの対数に比例する場合、アルゴリズムは対数時間で実行すると言われています。

例:バイナリ検索

「20の質問」ゲームを思い出してください-タスクは、間隔の中に隠された数の値を推測することです。推測を行うたびに、推測が高すぎるか低すぎるかが通知されます。20問の質問ゲームは、推定数を使用して間隔サイズを半分にする戦略を意味します。これは、バイナリサーチと呼ばれる一般的な問題解決方法の例です。

6. O(n 2)2次時間

アルゴリズムの実行時間が入力サイズの2乗に比例する場合、アルゴリズムは2次時間で実行すると言われています。

例:

7.役立つリンク


17
注:最初のリンクは壊れています。
Ziezi

2
O(n2)は混乱を避けるためにO(n ^ 2)と書く必要があります。
Rizki Hadiaturrasyid

100

この質問にはいくつかの良い答えがありますが。いくつかの例を挙げて、ここで別の回答を示しloopます。

  • O(n):ループ変数が一定量増加または減少する場合、ループの時間の複雑さはO(n)と見なされます。たとえば、次の関数はO(n)時間複雑です。

    // Here c is a positive integer constant   
    for (int i = 1; i <= n; i += c) {  
        // some O(1) expressions
    }
    
    for (int i = n; i > 0; i -= c) {
        // some O(1) expressions
    }
    
  • O(n ^ c):ネストされたループの時間の複雑さは、最も内側のステートメントが実行された回数と同じです。たとえば、次のサンプルループには O(n ^ 2)時間複雑です

    for (int i = 1; i <=n; i += c) {
       for (int j = 1; j <=n; j += c) {
          // some O(1) expressions
       }
    }
    
    for (int i = n; i > 0; i += c) {
       for (int j = i+1; j <=n; j += c) {
          // some O(1) expressions
    }
    

    たとえば、選択ソートと挿入ソートには O(n ^ 2)時間複雑です。

  • O(Logn)ループの時間の複雑さはO(Logn)と見なされますループ変数が分割されている場合/一定量を乗じました。

    for (int i = 1; i <=n; i *= c) {
       // some O(1) expressions
    }
    for (int i = n; i > 0; i /= c) {
       // some O(1) expressions
    }
    

    たとえば、バイナリ検索にはO(Logn)時間の複雑さがあります。

  • O(LogLogn)ループの時間複雑度は次のように考えられているO(LogLogn)ループ変数が低減されている場合/定量によって指数関数的に増加しました。

    // Here c is a constant greater than 1   
    for (int i = 2; i <=n; i = pow(i, c)) { 
       // some O(1) expressions
    }
    //Here fun is sqrt or cuberoot or any other constant root
    for (int i = n; i > 0; i = fun(i)) { 
       // some O(1) expressions
    }
    

時間の複雑さの分析の一例

int fun(int n)
{    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < n; j += i)
        {
            // Some O(1) task
        }
    }    
}

分析

For i = 1, the inner loop is executed n times. For i = 2, the inner loop is executed approximately n/2 times. For i = 3, the inner loop is executed approximately n/3 times. For i = 4, the inner loop is executed approximately n/4 times. ……………………………………………………. For i = n, the inner loop is executed approximately n/n times.

したがって、上記のアルゴリズムの合計時間の複雑さがある(n + n/2 + n/3 + … + n/n)になります、n * (1/1 + 1/2 + 1/3 + … + 1/n)

シリーズに関する重要なこと(1/1 + 1/2 + 1/3 + … + 1/n)O(Logn)と同じです。したがって、上記のコードの時間の複雑さはO(nLogn)です。


参照: 1 2 3


1
@サイモン、どの部分が間違っているのか分かりますか?
zangw

質問してくれてありがとう。コードを読み間違えました。コメントを削除しました。ごめんなさい!
Simon

74

例による時間の複雑さ

1-基本操作(算術、比較、配列の要素へのアクセス、代入):実行時間は常に一定O(1)

例:

read(x)                               // O(1)
a = 10;                               // O(1)
a = 1.000.000.000.000.000.000         // O(1)

2-if elseステートメント:2つ以上の可能なステートメントから最大実行時間のみを取得します。

例:

age = read(x)                               // (1+1) = 2
if age < 17 then begin                      // 1
      status = "Not allowed!";              // 1
end else begin
      status = "Welcome! Please come in";   // 1
      visitors = visitors + 1;              // 1+1 = 2
end;

したがって、上記の疑似コードの複雑さはT(n)= 2 + 1 + max(1、1 + 2)= 6です。したがって、その大きなohは一定のT(n)= O(1)です。

3-ループ(for、while、repeat):このステートメントの実行時間は、ループの数に、そのループ内の操作の数を掛けたものです。

例:

total = 0;                                  // 1
for i = 1 to n do begin                     // (1+1)*n = 2n
      total = total + i;                    // (1+1)*n = 2n
end;
writeln(total);                             // 1

したがって、その複雑さはT(n)= 1 + 4n + 1 = 4n + 2です。したがって、T(n)= O(n)です。

4-ネストされたループ(ループ内のループ):メインループ内に少なくとも1つのループがあるため、このステートメントの実行時間はO(n ^ 2)またはO(n ^ 3)を使用しました。

例:

for i = 1 to n do begin                     // (1+1)*n  = 2n
   for j = 1 to n do begin                  // (1+1)n*n = 2n^2
       x = x + 1;                           // (1+1)n*n = 2n^2
       print(x);                            // (n*n)    = n^2
   end;
end;

一般的な実行時間

アルゴリズムの分析には、いくつかの一般的な実行時間があります。

  1. O(1)–一定時間一定時間とは、実行時間が一定であることを意味し、入力サイズの影響を受けません

  2. O(n)–線形時間アルゴリズムがn個の入力サイズを受け入れる場合、n個の操作も実行します。

  3. O(log n)–実行時間O(log n)を持つ対数時間アルゴリズムは、O(n)よりわずかに高速です。一般に、アルゴリズムは問題を同じサイズのサブ問題に分割します。例:バイナリ検索アルゴリズム、バイナリ変換アルゴリズム。

  4. O(n log n)–線形時間この実行時間は、問題を再帰的にサブ問題に分割してn時間でマージする「分割統治アルゴリズム」でよく見られます。例:マージソートアルゴリズム。

  5. O(n 2)– 2次時間ルックバブルソートアルゴリズム!

  6. O(n 3)–キュービック時間O(n 2)と同じ原理です。

  7. O(2 n)–指数時間入力が大きくなると非常に遅くなります。n= 1000.000の場合、T(n)は21000.000になります。ブルートフォースアルゴリズムには、この実行時間があります。

  8. O(n!)–階乗時間THE SLOWEST !!! 例:旅行セールスマン問題(TSP)

この記事から引用。非常によく説明されているので、読んでください。


あなたの第二の例では、書いたvisitors = visitors + 1です1 + 1 = 2。なぜそうしたのか説明してください。
Sajib Acharya

3
@Sajib Acharya右から左に見てください。最初のステップ:計算visitors + 1 2番目のステップ:最初のステップからに値を割り当てるvisitors したがって、上記の式は2つのステートメントで形成されます。最初のステップ+ 2番目のステップ=> 1 + 1 = 2
Bozidar Sikanjic

@nbroなぜ1 + 1なのかage = read(x) // (1+1) = 2
Humty

@BozidarSikanjicなぜ1 + 1なのかage = read(x) // (1+1) = 2
Humty

1
@Humtyこの回答の冒頭を確認してください。read(x) // O(1) a = 10; // O(1)最初は関数呼び出し=> O(1)です///// 2番目はnbroが言ったように代入ですが、10は定数なので、2番目は=> O(1)...
ボジダルシカンジッチ2017年

41

コードを分析するときは、コードを1行ずつ分析し、すべての操作をカウントし、時間の複雑さを認識し、最終的に全体を把握するためにコードを合計する必要があります。

たとえば、線形の複雑さを持つ単純なループを1つ持つことができますが、同じプログラムの後半では、3 次の複雑さをもつ3つのループを作ることができるため、プログラムは3次の複雑さを持ちます。成長の関数順序がここで作用します。

アルゴリズムの時間の複雑さの可能性を見てみましょう。上で述べた成長の順序がわかります。

  • 一定の時間には、次のような成長の順序があり1ますa = b + c

  • 対数時間には成長の順序がありLogNます。通常、何かを半分に分割したり(二分探索、ツリー、ループなど)、同じように乗算したりします。

  • 線形、成長の順序はN、例えば

    int p = 0;
    for (int i = 1; i < N; i++)
      p = p + 2;
    
  • 線形、成長の順序はn*logN、通常、分割統治アルゴリズムで発生します。

  • キュービック、成長順N^3、古典的な例は、すべてのトリプレットをチェックするトリプルループです。

    int x = 0;
    for (int i = 0; i < N; i++)
       for (int j = 0; j < N; j++)
          for (int k = 0; k < N; k++)
              x = x + 2
    
  • 指数関数的な成長順は2^N、通常、あるセットのサブセットをチェックするなど、徹底的な検索を行うときに発生します。


この場合、複雑さはどうなるでしょうか。for(int i = 0; i <N; i ++)for(int j = i + 1; j <N; j ++)for(int k = j + 1; k <N; k ++)x = x + 2
user3156040

35

大まかに言えば、時間の複雑さは、入力サイズが増加するにつれて、アルゴリズムの操作またはランタイムの数がどのように増加するかを要約する方法です。

人生のほとんどのものと同様に、カクテルパーティーは私たちが理解するのに役立ちます。

オン)

パーティーに到着したら、全員の手を振る(すべてのアイテムに対して操作を行う)必要があります。参加者の数がN増えると、握手にかかる時間/作業が増えるのでO(N)

なぜO(N)ありませんかcN

人と握手するのにかかる時間にはばらつきがあります。これを平均化して、定数に取り込むことができcます。しかし、ここでの基本的な操作---みんなと握手---はO(N)、何があっても常にに比例しますcがあってます。カクテルパーティーに行くべきかどうかを議論するとき、私たちは多くの場合、ミーティングの様子の詳細よりも全員に会わなければならないという事実に関心があります。

O(N ^ 2)

カクテルパーティーの主催者は、みんなが他のみんなと出会う愚かなゲームをしたいと思っています。したがって、N-1他の人に会う必要があります。次の人がすでにあなたに会っているため、彼らはN-2人に会う必要があります。このシリーズの合計はx^2/2+x/2です。参加者の数が増えると、x^2用語が急速に増えるため、他のすべてを削除します。

O(N ^ 3)

あなたは他の全員と会う必要があり、各会議の間に、あなたは部屋の他の全員について話す必要があります。

O(1)

ホストは何かを発表したいと考えています。彼らはワイングラスを鳴らして大声で話します。誰もがそれらを聞きます。参加者の数は関係ありません。この操作には常に同じ時間がかかります。

O(ログN)

ホストは全員をアルファベット順にテーブルに配置しました。ダンはどこですか あなたは彼がアダムとマンディの間にいるはずだと考えています(確かにマンディとザックの間にではありません!)。それを考えると、彼はジョージとマンディの間にいますか?いいえ、彼はアダムとフレッドの間、そしてシンディとフレッドの間でなければなりません。など...セットの半分を見て、次にそのセットの半分を見ると、効率的にDanを見つけることができます。最終的に、O(log_2 N)を調べます個人。

O(NログN)

上記のアルゴリズムを使用して、テーブルのどこに座るかを見つけることができます。多数の人が一度に1人ずつテーブルに来て、全員がこれを行った場合、O(N log N)がかかります。時間かかります。これは、比較する必要があるアイテムのコレクションを並べ替えるのにかかる時間です。

ベスト/ワーストケース

あなたはパーティーに到着し、イニゴを見つける必要があります-どのくらい時間がかかりますか?いつ到着するかによります。誰もがあなたの周りを粉砕している場合、あなたは最悪のケースに遭遇しました:それにはO(N)時間がかかります。しかし、全員がテーブルに座っている場合、それはO(log N)時間がかかります。または、ホストのワイングラスの叫びのパワーを活用でき、O(1)時間はかかりません。

ホストが利用できないと仮定すると、到着時のパーティーの状態に応じて、Inigo検出アルゴリズムには下限と上限がO(log N)ありO(N)ます。

宇宙とコミュニケーション

アルゴリズムが空間や通信をどのように使用するかを理解するために、同じ考え方を適用できます。

Knuthは以前の"The Complexity of Songs"というタイトルの素晴らしい論文を書いています。

定理2:複雑なO(1)の任意の長さの曲が存在します。

証拠:(ケーシーとサンシャインバンドのため)。(15)で定義されている曲Skを考えますが、

V_k = 'That's the way,' U 'I like it, ' U
U   = 'uh huh,' 'uh huh'

すべてのk


あなたはそれを釘付けにしました、私がカクテルパーティーに行くときはいつでも、私は無意識のうちに楽しいイベントの時間の複雑さを見つけようとします。そのようなユーモラスな例をありがとう。
Sabunkar Tejas Sahailesh

5

私はこの質問が過去にさかのぼるのを知っており、いくつかの優れた答えがここにありますが、それでも私はこの投稿でつまずく数学を志向する人々のために別のビットを共有したいと思いました。マスターの定理は、複雑さを勉強したときに知っているもう一つの便利なものです。他の回答に記載されていませんでした。


2

O(n)は、アルゴリズムの時間の複雑さを記述するために使用されるビッグO表記です。アルゴリズムの実行回数を合計すると、2N + 2のような結果の式が得られます。この式では、Nが支配的な項です(この値は、値が増加または減少した場合に式に最大の影響を与えます)。ここで、O(N)は時間の複雑さであり、Nは項を支配しています。例

For i= 1 to n;
  j= 0;
while(j<=n);
  j=j+1;

ここで、内側のループの実行の合計数はn + 1で、外側のループの実行の合計数はn(n + 1)/ 2なので、アルゴリズム全体の実行の合計数はn + 1 + n(n + 1/2)です。 )=(n ^ 2 + 3n)/ 2。ここでn ^ 2は支配的な項なので、このアルゴリズムの時間の複雑さはO(n ^ 2)です。

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