web-dev-qa-db-ja.com

私の機能の時間の複雑さは何ですか?

複雑さについて勉強を始めましたが、私はこれに苦労しています:

_void what(int n) {
    int i;
    for (i = 1; i <= n; i++) {
        int x = n;
        while (x > 0)
            x -= i;
    }
}
_

さて、最初のforループは明らかにO(n)です。最初の反復はO(n)であり、2番目の反復はO(n/2) ..であり、そのようなlog(n)回ですか?これはO(n) * O(log(n)) = O(n * log(n)) complexityを意味します。これは正しかったですか?

編集:(重複ではない)Big Oとは何かを知っています。特定のケースで正しい評価を依頼しました。

89

外側のループはn回実行されます。

反復ごとに、内部ループは_n / i_回実行されます。

実行の総数は次のとおりです。

_n + n/2 + n/3 + ... + n/n
_

漸近的に(整数の算術丸めを無視して)、これは

_n * (1 + 1/2 + 1/3 + ... + 1/n)
_

この系列は、n * log(n)に向かって緩やかに収束します。

したがって、複雑さはO(N.log(N))予想どおりです。

106
chqrlie

最初の内部ループはn回実行されます
2番目の内部ループは_n/2_回実行されます
3番目の内部ループは_n/3_回実行されます
..最後のスクリプトは1回実行されます

n + n/2 + n/3 + ... + 1 = n(1+1/2+1/3+1/4 + ... +1/n)です。

これは、調和級数の合計で乗算されたnで、ニース閉形式表現を持ちません。しかし、示されているように here それはO(log(n))です。したがって、全体的にアルゴリズムはO(n log(n))です

34
Eugene Sh.

別の方法として、変数置換_y = n - x_の後にシグマ表記法を使用して、アルゴリズムの内部whileループの反復回数を分析します。

enter image description here

上記の各whileループについて、_1_がiの倍数でない場合、つまり_n-1_の場合、_(n-1) % i != 0_による反復回数を過大評価します。進むにつれて、_(n-1)/i_はiのすべての値の整数であると仮定します。したがって、内部のwhileループの反復の合計数を過大評価し、その後、または以下の_(i)_の等号。シグマ表記分析を続行します。

enter image description here

ここで、_(ii)_で、n:th 調和数 を関連する積分で近似しました。したがって、アルゴリズムはO(n·ln(n))で漸近的に実行されます。


漸近的な振る舞いを残し、アルゴリズムの実際の成長を調べるために、正確な_(n,cnt)_(ここでcntは内部反復の数)ペアのNiceサンプルデータを@chuxで使用できます(彼の答えを参照)。そして、上記からの推定反復回数、つまりn(1+ln(n))-ln(n)と比較します。推定が実際のカウントときれいに調和していることは明らかです。下のプロットまたは 実際の数値のスニペット を参照してください。

enter image description here


最後に、上記の_n->∞_の合計に_1/i_を入れた場合、結果の系列は 無限高調波系列 であり、これは不思議なことに、発散することに注意してください。後者の証明は、非ゼロ項の無限和では自然に無限であるという事実を利用しています。ただし、実際には、十分に大きいが無限ではないnの場合、ln(n)は合計の適切な近似であり、この発散ここでの漸近解析には関係ありません。


18
dfri

テストとグラフィックスによりこれを試みます。 log2対log2のプロットはかなり線形に見えます。線形O(n)以上O(n * log(n))未満)の間の何か。独自の結論を「引き出し」ます。

[編集]

数学的に導出された式を使用すると、O()計算された値はO(n * log(n))の上限になります。たとえば、nが6の場合、反復カウントは実際の6 + 3 + 2 + 2 + 2 + 1 = 16ではなく、6 + 3 + 2 + 1.5 + 1.2 + 1 = 14.7ループです。 nが増加するので、差は比較的小さいので、O(n * log(n))の成長よりもわずかに小さいため、整数演算を使用するコードを無視しないことで、はるかに難しい質問があります。


enter image description here

unsigned long long what(int n) {
  unsigned long long cnt = 0;
  int i;
  for (i = 1; i <= n; i++) {
    int x = n;
    while (x > 0) {
      x -= i;
      cnt++;
    }
  }
  return cnt;
}

void wtest(int n) {
  unsigned long long cnt = what(n);
  printf("%d %llu\n", n, cnt);
  fflush(stdout);
}

void wtests(void) {
  int i = INT_MAX/2 + 1;
  while (i > 0) {
    wtest(i);
    i /= 2;
  }
}

int main(void) {
  wtests();
  return 0;
}

出力

1073741824 23567395117
536870912 11411566988
268435456 5519718329
134217728 2666826555
67108864 1286897093
33554432 620190504
16777216 298466265
8388608 143418602
4194304 68802063
2097152 32947406
1048576 15746897
524288 7510048
262144 3573331
131072 1695816
65536 802493
32768 378537
16384 177921
8192 83286
4096 38803
2048 17973
1024 8275
512 3782
256 1713
128 765
64 337
32 145
16 61
8 24
4 9
2 3
1 1
11
chux