【问题标题】:Why does my Fibonacci take so long when the dictionary is in the function?当字典在函数中时,为什么我的斐波那契需要这么长时间?
【发布时间】:2019-11-29 22:32:49
【问题描述】:
这里我有一些代码可以返回斐波那契数的最后一位。当我将缓存字典放在函数中时,程序对于小 n 运行良好。当我尝试像 300 这样更大的 n 时,程序需要永远。但是,当我将字典设为全局时,我会立即获得更大的 n(例如 300)的结果。是什么导致在函数中声明的字典与在函数外部声明的字典之间存在如此明显的性能差异?
def fib_last_digit_mem(n):
cache = {}
if n in cache:
return cache[n]
if(n <= 1):
return n
fib = (fib_last_digit_mem(n-1) + fib_last_digit_mem(n-2))%10
cache[n] = fib
return fib
【问题讨论】:
标签:
python
algorithm
dictionary
fibonacci
【解决方案1】:
由于这是一个递归函数,如果你在函数内部实例化缓存,它将在每次函数递归时再次实例化。这也意味着缓存总是空的,所以你永远不会走捷径if n in cache
【解决方案2】:
您实际上并没有缓存任何内容,因为每次调用函数时 local var cache 都会初始化为空字典。当然,一旦你计算出你将它添加到字典中的值。但随后您返回:cache 超出范围,dict 被垃圾回收。
您需要对存在于外部 fib_last_digit_mem 的cache 的一些引用,但这不一定是全局变量。
考虑:
def make_cached_fib():
cache = {}
def _(n):
if n in cache:
return cache[n]
if n <= 1:
return n
fib = (_(n-1) + _(n-2)) % 10
cache[n] = fib
return fib
return _
fib_last_digit_mem = make_cached_fib()
这里的缓存不是全局的;它在定义fib_last_digit_mem 的范围内。一旦make_cached_fib 返回,对缓存的唯一引用就是fib_last_digit_mem 本身持有的那个。
【解决方案3】:
您在递归调用的函数中实例化缓存。这会导致两个问题:
1) 每次进行递归调用时,都必须实例化一个缓存,这会消耗一些性能,但创建过程并不是代码运行缓慢的原因。
2) 真正的原因和更大的问题是您没有对缓存做任何事情。动态编程允许您重用以前计算的结果,因此您不必再次计算它们。您将这些结果保存在缓存中。但是因为每次调用该方法时都会实例化缓存,所以所有递归调用最终都会拥有自己的空本地缓存,而不是共享一个全局缓存,这有助于避免再次计算先前计算的结果。
例子:
如果你全局声明缓存并计算fibonacci(10),最后一个操作只需将fibonacci(9)的结果从缓存中取出并添加另一个数字。它只是计算下一个第 n 个斐波那契数的加法。在你的情况下,而不是仅仅得到 fibonacci(9) 的结果并添加一个数字,你实际上是在计算 fibonacci(9) ,这意味着你必须再次计算 fibonacci(8) 等等......这会导致没有动态的糟糕表现编程(全局缓存)