是的。原始递归解决方案需要大量时间。这样做的原因是,对于每个计算的数字,它需要多次计算之前的所有数字。看看下面的图片。
它代表Fibonacci(5)
用你的函数计算。如您所见,它计算了Fibonacci(2)
3 倍的值和Fibonacci(1)
5 倍的值。您想要计算的数字越高,情况就会变得越来越糟。
更糟糕的是,对于您在列表中计算的每个斐波那契数,您不会使用您所知道的先前数字来加速计算——您“从头开始”计算每个数字。
有几个选项可以加快速度:
1.“自下而上”创建列表
最简单的方法是创建一个斐波那契数字列表,直到您想要的数字。如果你这样做,你可以“自下而上”构建,并且可以重复使用以前的数字来创建下一个。如果您有斐波那契数列[0, 1, 1, 2, 3]
,您可以使用该列表中的最后两个数来创建下一个数。
这种方法看起来像这样:
>>> def fib_to(n):
... fibs = [0, 1]
... for i in range(2, n+1):
... fibs.append(fibs[-1] + fibs[-2])
... return fibs
...
然后你可以通过做得到前 20 个斐波那契数
>>> fib_to(20)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
或者您可以通过执行从前 40 个列表中获取第 17 个斐波那契数
>>> fib_to(40)[17]
1597
2. 记忆(比较先进的技术)
存在另一种使其更快的替代方法,但它也有点复杂。由于您的问题是您重新计算已经计算的值,因此您可以选择将已经计算的值保存在 dict 中,并在重新计算之前尝试从中获取它们。这称为记忆。它可能看起来像这样:
>>> def fib(n, computed = {0: 0, 1: 1}):
... if n not in computed:
... computed[n] = fib(n-1, computed) + fib(n-2, computed)
... return computed[n]
这使您可以轻而易举地计算大斐波那契数:
>>> fib(400)
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
这实际上是一种常见的技术,以至于 Python 3 包含一个装饰器来为您执行此操作。我介绍给你,自动记忆!
import functools
@functools.lru_cache(None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
这与前一个函数的作用几乎相同,但所有的computed
东西都由lru_cache
装饰器处理。
3. 数数(一个简单的迭代解决方案)
第三种方法,如 Mitch 所建议的,是只计数而不将中间值保存在列表中。你可以想象做
>>> def fib(n):
... a, b = 0, 1
... for _ in range(n):
... a, b = b, a+b
... return a
如果您的目标是创建斐波那契数列,我不推荐最后两种方法。fib_to(100)
将比后者快得多,[fib(n) for n in range(101)]
因为使用后者,您仍然会遇到从头开始计算列表中每个数字的问题。