再帰ツリーを使用してフィボナッチ数列の複雑さを見つけようとし、height of tree = O(n)
最悪の場合、_cost of each level = cn
_、したがって_complexity = n*n=n^2
_
どうしてO(2^n)
なのですか?
単純な再帰フィボナッチの複雑さは実際に2ⁿです。
_T(n) = T(n-1) + T(n-2) = T(n-2) + T(n-3) + T(n-3) + T(n-4) =
= T(n-3) + T(n-4) + T(n-4) + T(n-5) + T(n-4) + T(n-5) + T(n-5) + T(n-6) = ...
_
各ステップでT
を2回呼び出すため、次のような漸近的な障壁が得られます。T(n) = 2⋅2⋅...⋅2 = 2ⁿ
bonus:フィボナッチへの最適な理論的実装は、実際には close formula で、 golden ratio を使用します:
_Fib(n) = (φⁿ – (–φ)⁻ⁿ)/sqrt(5) [where φ is the golden ratio]
_
(ただし、実際には浮動小数点演算による精度エラーが発生しますが、正確ではありません)
Fib(n)の再帰ツリーは次のようになります。
n
/ \
n-1 n-2 --------- maximum 2^1 additions
/ \ / \
n-2 n-3 n-3 n-4 -------- maximum 2^2 additions
/ \
n-3 n-4 -------- maximum 2^3 additions
........
-------- maximum 2^(n-1) additions
t(n)= t(n-1)+ t(n-2)これはツリー法で解くことができます:
t(n-1) + t(n-2) 2^1=2
| |
t(n-2)+t(n-3) t(n-3)+t(n-4) 2^2=4
. . 2^3=8
. . .
. . .
同様に最後のレベル。 。 2 ^ n
合計時間の複雑度=> 2 + 4 + 8 + ...
このように見てください。 F(k)
(kth
フィボナッチ数)を再帰的に計算する複雑さは、最大_2^k
_が_k <= n
_であると仮定します。これが私たちの帰納法の仮説です。次に、再帰によってF(n + 1)
を計算する複雑さは
_F(n + 1) = F(n) + F(n - 1)
_
複雑さは2^n + 2^(n - 1)
です。ご了承ください
_2^n + 2^(n - 1) = 2 * 2^n / 2 + 2^n / 2 = 3 * 2^n / 2 <= 2 * 2^n = 2^(n + 1).
_
帰納法により、再帰によるF(k)
の計算は最大で_2^k
_であるという主張が正しいことを示しました。
ツリーの深さはO(n)であることは正しいですが、各レベルでO(n)作業を行っているわけではありません。各レベルでO(1)再帰呼び出しごとに動作しますが、各再帰呼び出しは、その下のレベルとその下のレベル2の2つの新しい再帰呼び出しに貢献します。ツリーでは、レベルごとの呼び出しの数は指数関数的に増加します。
興味深いことに、F(n) as 2F(n + 1)-1を計算するのに必要な正確な呼び出し数を実際に確立できます。ここで、F(n) =はn番目のフィボナッチ数です。帰納的に証明できます。基本ケースとして、F(0)またはF(1)を計算するには、関数を1回呼び出す必要があります。 L(n)はF(n)を計算するのに必要な呼び出しの数だとしましょう。
L(0)= 1 = 2 * 1-1 = 2F(1)-1 = 2F(0 + 1)-1
L(1)= 1 = 2 * 1-1 = 2F(2)-1 = 2F(1 + 1)-1
ここで、帰納的ステップについて、すべてのn '<nで、n≥2で、L(n')= 2F(n + 1)-1であると仮定します。次に、F(n)を計算するために、1 F(n)を計算する初期関数を呼び出します。これにより、F(n-2)およびF(n-1)への呼び出しが開始されます。帰納的仮説により、 F(n-1) and F(n-2)は、L(n-1) and L(n-2)呼び出し。したがって、合計ランタイムは
1 + L(n-1)+ L(n-2)
= 1 + 2F((n-1)+ 1)-1 + 2F((n-2)+ 1)-1
= 2F(n)+ 2F(n-1)-1
= 2(F(n) + F(n-1))-1
= 2(F(n + 1))-1
= 2F(n + 1)-1
これで誘導が完了します。
この時点で、 Binetの式 を使用して、
L(n) = 2(1/√5)(((1 + √5) / 2)n -((1-√5)/ 2)n)-1
したがって、L(n) = O(((1 +√5)/ 2)n)。次の規則を使用する場合
φ=(1 +√5)/ 2≈1.6
私たちはそれを持っています
L(n)=Θ(φn)
そして、φ<2なので、これはo(2n)(little-o表記を使用)。
興味深いことに、このシリーズの名前はL(n)を選択しました。このシリーズは Leonardo numbers と呼ばれるためです。ここでの使用に加えて、 smoothsort アルゴリズムの分析で発生します。
お役に立てれば!
再帰フィボナッチ数列の複雑さは2 ^ nです。
これは再帰的フィボナッチの再帰関係になります
_ T(n)=T(n-1)+T(n-2) No of elements 2
_
次に、置換方法を使用してこの関係を解決します(T(n-1)およびT(n-2)の値を代入))
_T(n)=T(n-2)+2*T(n-3)+T(n-4) No of elements 4=2^2
_
再び上記の用語の値を代入すると、次のようになります
_T(n)=T(n-3)+3*T(n-4)+3*T(n-5)+T(n-6) No of elements 8=2^3
_
完全に解決した後、
_T(n)={T(n-k)+---------+---------}----------------------------->2^k eq(3)
_
これは、どのレベルでも再帰呼び出しの最大数が最大2 ^ nであることを意味します。
式3のすべての再帰呼び出しではfor(1)であるため、時間の複雑さは2^n* ϴ(1)=2^n
になります
フィボナッチ数列の複雑さはO(F(k))です。ここで、F(k)はk番目のフィボナッチ数です。これは帰納法によって証明できます。ベースの場合は簡単です。そして、すべてのk <= nについて、F(k)を計算する複雑さはc * F(k)+ o(F(k))であり、k = n + 1については、 F(n + 1)の計算はc * F(n)+ o(F(n)) + c * F(n-1)+ o(F(n-1)) = c *(F(n)+ F(n-1)) + o(F(n)) + o(F(n-1)) = O(F(n + 1))。
フィボナッチ数計算のO(2 ^ n)複雑性は、再帰アプローチにのみ適用されます。いくつかの余分なスペースを使用すると、O(n)ではるかに優れたパフォーマンスを実現できます。
public static int fibonacci(int n) throws Exception {
if (n < 0)
throws new Exception("Can't be a negative integer")
if (n <= 1)
return n;
int s = 0, s1 = 0, s2 = 1;
for(int i= 2; i<=n; i++) {
s = s1 + s2;
s1 = s2;
s2 = s;
}
return s;
}
Fibの線形時間反復アルゴリズムを指数時間再帰アルゴリズムに接続する誘惑に抵抗することはできません。JonBentleyの「Writing Efficient Algorithms」に関するすばらしい本を読めば、「キャッシング」の単純なケースだと思います: k)計算され、配列FibCached [k]に保存します。 Fib(j)が呼び出されるたびに、まずFibCached [j]にキャッシュされているかどうかを確認します。はいの場合、値を返します。再帰を使用しない場合。 (今すぐ呼び出しのツリーを見てください...)