为什么这两种寻找第 n 个斐波那契数的算法中的一种更有效?

Why is one of these two algorithms for finding the n-th Fibonacci number more efficient?

计算第64个斐波那契数时,第一个算法需要几个小时,第二个不到一秒。

为什么第二种算法的效率比第一种算法高这么多?

他们看起来很像。

def fib_divide_recursion(n):
    if n <= 2:
        return n-1
    else:
        return fib_divide_recursion(n-1) + fib_divide_recursion(n-2)

def fib_linear_recursion(n, prev={}):
    if n <= 2:
        return n-1
    try:
        return prev[n]
    except KeyError:
        prev[n] = fib_linear_recursion(n - 1, prev) + 
            fib_linear_recursion(n - 2, prev)
        return prev[n]

第一个算法的复杂度是O(2^n)。

第二个将结果缓存在 prev 中,因此它永远不会为给定数字计算 fib_linear_recursion 超过一次。它的复杂度是线性的,O(n)。

有关详细信息,请参阅 this answer

第二个实现是使用 "memoizing" 来记住之前计算的斐波那契值。

假设您正在尝试计算 fib(5):您首先必须计算 fib(4)fib(3)fib(4)本身也需要你去计算fib(3)。事实上,对于每一个斐波那契数,你要么计算前面的每一个斐波那契数一次并存储它们(这就是记忆方法)。或者,在性能更差的情况下,您可以重新计算所需的每个斐波那契数,即使您之前已经计算过它也是如此。显然,如果没有记忆,您将需要做更多的工作,对于高斐波那契数,这确实会有所不同,正如您所观察到的那样。

第一种算法仅使用递归,而第二种算法使用动态规划,即带记忆的递归。

如果您为第一个算法画一棵树,您会看到重复的节点。但是使用第二种算法,它存储已经计算出的节点,因此程序不必一次又一次地计算