2012-03-09 6 views
4

私は再帰とキャッシングを理解しようとしていますが、まだまだ私は興味深い進歩を遂げています。私はこのコードを理解するのに少し問題があります:フィボナッチコードからの再帰とキャッシングを理解できない

# Fibonacci recursive with result storage 

class FibonacciStorage: 
    _storage = { 0:1, 1:1 } 
    @staticmethod 
    def _fib(number): 
     try: # is this already calculated 
      return FibonacciStorage._storage[number] #searches dict, and if value exists then return it 
     except KeyError: 
      result = FibonacciStorage._fib(number-1) + FibonacciStorage._fib(number-2) #this is the actual Fibonacci Formula 
      FibonacciStorage._storage[number] = result #adds result to dictionary 
      #print FibonacciStorage._storage #this shows the storage list growing with each iteration. 
      return result 
    @staticmethod 
    def fib(number): #first function, it has two asserts to basically make sure number is whole/positive and if its okay passes it to _fib(where the real work is done) 
     # only do the assert statements once 
     assert(isinstance(number,int)),"Needs a whole number" 
     assert(number>0),"Needs a positive whole number" 
     return FibonacciStorage._fib(number) 

# use a more readable name 
getFib = FibonacciStorage.fib 

print getFib(50) 

私はこのコードをネットで見つけて、実際に何をしているのかを理解するために各行にコメントしようとしました。私はこれがどのように再帰であるのか分かりませんが、正しい結果が得られるまでコードをループしますが、その呼び出し自体は見えません。

私はそれがこの行だと思っていましたが、私のprint FibonacciStorage._storage行はストレージキャッシュの成長と成長を示していますが、その下の行はリターン関数なので混乱します。 return resultがどういうふうに再帰を再び引き起こしているのか分かりません。

また、ストレージ辞書がこのプログラムを非常に速くしているかどうかもわかりません。ファイブシーケンスの単純な再帰は(データを保存しても)長い時間がかかりますが、この場合はprint getFib(200)の瞬間があります。キャッシングはどのように高速化していますか?

だからまとめるために、二つの質問は:

1)どのように再帰は実際にトリガされていますか?私のprintステートメントが各ループでアクセスされている場合?

2)キャッシングは他のフィブスシーケンスよりもどのように高速化されますか?クラス構造や@staticmethodが違いを生み出していますか?例えば、これは瞬間的で、http://en.literateprograms.org/Fibonacci_numbers_(Pythonのものは少し遅れているようです。

3)おそらくintrestから出て、ちょうどintrestから出ているfibnocciタイプのalgoスケール(分割して並行して実行できますか?

洞察力があれば助かります。

+2

再帰を理解するには良い試みです。フィボナッチシリーズの定義は再帰的ですが、再帰的な実装は非常に無駄であるため、パターンパターンです。 'fib(7)'が24回の呼び出しを行い、 'fib(3)' _five_ timesの値を独立して計算することを考慮してください。実際、 'fib(n)'に対する呼び出しの総数は '2 * fib(n) - 1'です。 'fib(20)'(6765)を計算するには13,529回の呼び出しが必要です。 –

+0

Python 2.xを使用している場合は、常にクラスを 'object'から継承します。そうでなければ微妙なエラーが発生します(例えば、" staticmethod "がクラスで動作しないなど) – jsbueno

+0

@JimGarrisonは再帰上記のようにキャッシュすれば悪いですか?私はそれを理解しようとしていますが、これまでのところ理解していないので、ほとんどのものはキャッシュに対してルックアップされているので、20回以上しかループしません。 –

答えて

5

(1)再帰呼び出しは、この行にある:

result = FibonacciStorage._fib(number-1) + FibonacciStorage._fib(number-2) 
//     here^      and here^

そうだね、これは、定義によって、再帰的である「実際のフィボナッチ式」、です。

(2)キャッシングが高速化しています。違いを生むのは永続オブジェクト_storageです。これはの驚異の重複作業量を節約しています。考えてみましょう:

        fib(4) 
          /  \ 
          fib(3) +  fib(2) 
         / \  / \ 
        fib(2) + fib(1) fib(1) + fib(0) 
        / \ 
       fib(1) + fib(0) 

これは単なるfib(4)で、すでに冗長性を見ることができます。 fib(3)fib(4)の整数値を単にキャッシュするだけで、fib(5)に必要な計算回数を7から1に減らすことができます。

+0

しかし、その場合、再帰に行くべきではないでしょうか?私のプリントステートメントは以下の通りですか?あなたがそれをコメント解除すると、あなたはそれが毎回増えるのを見るでしょう。 –

+0

私はそれが何をしているのか理解しています。私はその背後にあるプログラミングの側面を理解することに躊躇しています。例えば、キャッシングが速くなると理解していますが、なぜこのキャッシングが、このサイトのhttp://en.literateprograms.org/Fibonacci_numbers_(Python) –

+0

@learningJava 'fib'の' return'sへの再帰呼び出しが行われたときに 'fib'で中断された箇所だけがピックアップされますコール.. – paislee

2

フィボナッチの実装では、すでに計算された値を保存し、必要なときに効率的に検索するために、再計算する代わりにmemoizationという手法を使用します。値は一度だけ計算する必要があるため、パフォーマンスに大きなメリットがあります。再帰がこのラインでトリガされ

  1. :ご質問について

    あなたは_fib手順はキャッシュが大幅に計算速度を向上さ

  2. 自身を呼び出している見ることができるよう、FibonacciStorage._fib(number-1) + FibonacciStorage._fib(number-2)、これを参照してくださいexampleを参照してください。この場合、静的メソッドを使用することは特に有用ではなく、インプリメンテーションをより工夫します。
  3. memonizedではない再帰的なバージョンは、並列アルゴリズムで分割され、fibonacciの呼び出しは別のスレッドで実行されます。しかし、オーバーヘッドはかなり大きく、アルゴリズムの複雑さはまったく改善されません。フィボナッチを計算するためのより速い方法については、メモしたバージョンに固執するか、read
+0

オスカーはメモを聞いたことはありませんでしたが、私が知りたいと思っていたようなものです。感謝します。しかし、線維再発に関しては、それも呼んでいると思っていました。しかし、あなたが私のprintステートメントのコメントを外すと、値が増えていることがわかります。これは、printステートメントがすべてのループで実行されていると思います。その場合、FibonacciStorage._fib(number-1)は実際にそれをどのように呼び出していますか?それがあった場合、試し文が –

+0

になるまで辞書キャッシュを構築するため、私のprint文は決して実行されません。try文が動作すれば、結果を返して終了します。 ? FibonacciStorage._fib(number-1)がすぐに呼び出されていれば?または遅延がある(再帰を行う前に関数が終了するのを待つか)?申し訳ありませんが、これは私が混乱しているところです。 –

+0

値がキャッシュにない場合は、追加された直後にprintステートメントが呼び出されます。私はあなたがそれをよりよく理解するためにプログラムの実行をトレースする必要があると思います、デバッガを使用してください。 –

1

それはPythonのdictsが不足しているキーのために呼び出されます__missing__方法を持っていることを知って良いことだ:

class FibonacciStorage(dict): 
    def __missing__(self, n): 
     self[n] = self[n - 1] + self[n - 2] 
     return self[n] 

使用法:

fib = FibonacciStorage({0: 1, 1: 1}) 
print(fib[50]) #20365011074