web-dev-qa-db-ja.com

アルゴリズムのメモリと時間の複雑さを判断する方法は?

私は時間と記憶の複雑さを判断するのが苦手です。誰かが私を助けてくれれば幸いです。

私はここにアルゴリズムを持っていますが、その時間とメモリの複雑さがどうなるかわかりません。

Function sample(k)
   IF k < 2
       Return 0
   Return 1 + sample(k/2)

その時間とメモリの複雑さは何ですか、そしてその理由は何ですか?

ありがとう

12
user3138212

時間とメモリの複雑さを判断すると、countingアルゴリズムの実行時にこれら2つのリソースのどれだけが使用され、これらの量が入力サイズとしてどのように変化するかを確認できます(kこの場合) )変更。

時間は各命令が評価される回数によって決定され、スペースは関連するデータ構造がソリューションを計算するために必要な大きさによって決定されます。

この特定のシナリオでは、再帰的アルゴリズムを検討しているため、基本的に、これには1)行われた再帰的呼び出しの数、および2)これらの呼び出しごとに行われた作業の量をカウントすることが含まれます。

入力は呼び出しごとにhalvedであるため、呼び出しのシーケンスは次のようになります。

_sample(k)       )
 sample(k/2)    )
  sample(k/4)   ) 
     ...        ) - n = number of calls 
    sample(4)   )
     sample(2)  )
      sample(1) )
_

このように各再帰呼び出しで半分にすると、logarithmic呼び出し数になります。

_n = log(k)
_

呼び出しごとに、constant数の変数を呼び出しスタックに格納し、一定量の作業(操作)を実行します。これは、変数の数と比較/追加/除算の数各呼び出しでが大きくなっても大きくならないという事実に起因しますk

合計時間計算量は、呼び出しの数に各呼び出しで実行された作業量を掛けたものです。

_time complexity = A*log(k) + B
_

再帰呼び出しを実行し、比較/除算をそれぞれ実行する実際の時間コストを反映するいくつかの定数AおよびBの場合。同様に:

_space complexity = C*log(k) + D
_

再帰のスペースコストと可変ストレージのそれぞれに適した定数CとDの場合。

この種の分析では、主に漸近的な複雑さを気にします。つまり、定数はアルゴリズムを実行しているマシンの詳細を反映しているため、定数はあまり気にしません。曲線の形状を知りたいのです( kが大きくなるにつれて)。 Big-Oh表記を使用して複雑さを書く規則に従うと、次の結果が得られます。

_space complexity = time complexity = O(log(k))
_

編集:メモリの複雑さの詳細

前に述べたように、メモリの複雑さは、ソリューションを計算するために必要なデータ構造の大きさによって決まります。したがって、この関数で使用されているデータ構造はないので、log(k)メモリはどこに行きますか?

簡単な答え:log(k)パラメータkの異なる値を、再帰呼び出しごとに1つずつ格納する必要があります。

詳細な答え:関数呼び出しのメカニズムによってここで使用されている暗黙のデータ構造があります(再帰を介して利用します) )およびその名前はコールスタックです。 sample(k)が呼び出されるたびに、新しいスタックフレームが作成され、パラメータkのローカル値、リターンアドレス、およびその他の実装に依存するいくつかの値がスタックにプッシュされます。物事。このようにして、各再帰呼び出しは、そのローカル情報が格納されているスタック上に「レイヤー」を形成します。全体像は次のようになります。

_----------- < top of stack
|  k = 1  |
|   ...   | < stack frame for sample(1)
|---------|
|  k = 2  |
|   ...   | < stack frame for sample(2)
|---------|

    ...

|---------|
| k = p/2 |
|   ...   | < stack frame for sample(p/2)
|---------|
|  k = p  |
|   ...   | < stack frame for sample(p)
|---------|
|         | < stack frame for main() or whatever 
              initially called sample(p) 
              (we don't count this one)
_

(ここでは、混乱を避けるために、再帰呼び出しごとに初期パラメーター値pkの値を区別しました)

注意すべき主な点は、n = log(k)再帰呼び出しがあるため、そのようなスタックフレームはnあるということです。各スタックフレームのサイズは一定であるため、スペースの複雑さはO(log(k))です。

19
Ezequiel Muns

あなたは本当にlog_2(k)、基数2の対数を見ています。基数を変更するには、定数を掛ける必要があります。とにかく定数を掛けるので、O(log(n))、O(ln(n))、O(log_2(n))はすべて同じです。

では、なぜ上記の方法は2を底とする対数の複雑さを持っているのでしょうか?呼び出しごとにkを半分に分割します。逆方向に進むと、すべての呼び出しで2が乗算されます。 2を掛けると2 ^ nになり、log_2(n)はその逆になります。

二分木を描くと役立つかもしれません。nノードのツリーの高さはlog_2(n)で、高さnのツリーのノード数は2 ^ nです。

0
user835611