无法理解Fibonacci代码的递归和缓存

时间:2012-03-09 03:21:44

标签: python algorithm

我试图更好地理解递归和缓存,但我仍然取得了有趣的进展(有时候我有点慢)。我在理解这段代码时遇到小问题:

# 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)

我在网上找到了这段代码,并试图评论每一行,以了解它实际上在做什么。我不明白这是一个递归,它会循环遍历代码,直到给出正确的结果,但我不知道它是如何调用自己的。

我认为这是第一行:result = FibonacciStorage._fib(number-1) + FibonacciStorage._fib(number-2)但我很困惑,因为我的print FibonacciStorage._storage行显示存储缓存增长和增长,但它下面的行是返回函数。我怀疑我不明白return result实际上是如何以某种方式再次触发递归。

我也不确定存储字典是如何让这个程序变得如此之快。我对fib序列的简单递归需要很长时间(即使我保存数据),但在这种情况下,如果你做了print getFib(200)它的瞬间。缓存如何使它如此之快?

总而言之,有两个问题:

1)如何实际触发递归?如果在每个循环中访问我的print语句?

2)缓存如何比其他纤维序列加速?类结构或@static方法有所不同吗?例如,这似乎是即时的http://en.literateprograms.org/Fibonacci_numbers_(Python)稍有延迟。

3)也许还有一点兴趣,一个fibnocci类型的算法规模(分开并且并行运行)只是出于兴趣还是仅限于一个过程?

任何见解都会有所帮助。

3 个答案:

答案 0 :(得分:5)

(1)递归调用就在这一行:

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

你是对的,这是"实际的Fibonacci公式",根据定义是递归的。

(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.并且只有当您更高时才会节省成本

答案 1 :(得分:2)

问题中的斐波那契实现使用一种名为memoization的技术来存储已经计算过的值,并在每次需要时有效地检索它们,而不是重新计算它们。这具有巨大的性能优势,因为值只需要计算一次。

关于您的问题:

  1. 在此行中触发递归:FibonacciStorage._fib(number-1) + FibonacciStorage._fib(number-2),因为您可以看到_fib过程正在调用自身
  2. 缓存大大提高了计算速度,请参阅此example以获取有关其工作原理的完整说明。在这种情况下,使用静态方法并不是特别有用,它只会使实现更加节制
  3. 递归的,非memoized版本可能会在并行算法中拆分,其中每次调用fibonacci都在不同的线程上执行。然而,开销将是相当大的,并且算法复杂性根本不会得到改善。坚持使用memoized版本,或read关于计算斐波纳契的更快方法。

答案 2 :(得分: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