【发布时间】:2012-03-12 07:26:40
【问题描述】:
我正在尝试更好地理解递归和缓存,但我仍然取得了有趣的进展(有时我有点慢)。我在理解这段代码时遇到了一些小问题:
# 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) 递归实际上是如何被触发的?如果每个循环都访问我的打印语句?
2) 与其他 fib 序列相比,缓存如何加快速度?类结构或@staticmethod 有影响吗?例如,这似乎是即时的,而 http://en.literateprograms.org/Fibonacci_numbers_(Python) 上的那些有轻微的延迟。
3) 也可能出于兴趣,fibnocci 类型的算法规模(拆分并并行运行)是否只是出于兴趣,还是仅限于一个过程?
任何见解都会有所帮助。
【问题讨论】:
-
理解递归是一个很好的尝试。斐波那契数列的定义是递归的,但递归实现是一种反模式,因为它非常浪费。考虑
fib(7)将导致24 次调用,并将独立计算fib(3)的值五次 次。事实上,fib(n)的调用总数是2 * fib(n) - 1。计算fib(20)(6765) 需要 13,529 次调用。 -
如果您使用的是 Python 2.x,请始终从
object继承您的类 - 否则您会收到一些细微的错误(例如发现“staticmethod”在您的类中不起作用,或其他) -
@JimGarrison 如果像上面这样缓存,递归会不会那么糟糕?我试图理解它,但据我所知,到目前为止它并没有重复它的任何工作,所以 fib(20) 不会导致只有超过 20 个循环,因为它们中的大多数都是针对缓存的查找?
-
是的,缓存结果会有所改善。但是您仍然需要拨打
2*fib(n)-1电话。一个简单的迭代解决方案是唯一的方法。这只是一个警示故事,请记住对于一些递归问题,线性解决方案更好。 -
你的理解已经超出了基础,但你可能会觉得my answer to a similar question很有趣。