我从不同的教程站点上看到了Fibonacci的几种解决方案,我注意到的一件事是,它们具有通过递归函数解决问题的相同方法。我测试了递归函数,它花了77秒才获得列表中的第40个项目,因此我尝试制作一个函数而不通过for循环处理递归函数,并且只用了不到一秒钟的时间。我说对了吗?我的功能的O符号是什么?
from time import time
def my_fibo(n):
temp = [0, 1]
for _ in range(n):
temp.append(temp[-1] + temp[-2])
return temp[n]
start = time()
print(my_fibo(40), f'Time: {time() - start}')
# 102334155 Time: 0.0
vs
from time import time
def recur_fibo(n):
if n <= 1:
return n
else:
return recur_fibo(n - 1) + recur_fibo(n - 2)
start = time()
print(recur_fibo(40), f'Time: {time() - start}')
# 102334155 Time: 77.78924512863159
答案 0 :(得分:2)
您所做的是时空权衡的一个示例。
在第一个(迭代)示例中,您有一个O(n)时间算法,该算法也占用O(n)空间。在此示例中,您存储了值,因此您无需重新计算它们。
在第二个(递归)示例中,您有一个O(2 ^ n)时间的算法(有关更多详细信息,请参见Computational complexity of Fibonacci Sequence),该算法也占用了堆栈上的大量空间。
在实践中,后一个递归示例是处理Fibonacci序列的“幼稚”方法,存储先前值的版本明显更快。
答案 1 :(得分:0)
对于big-O,答案就在上面:在您展示的经典递归实现中,该函数在每次遍历中两次调用自身。
在下面的示例中,我编写了一个递归函数,该函数在每次通过中仅调用一次,因此它也具有O(n):
def recur_fibo3(n, curr=1, prev=0):
if n > 2: # the sequence starts with 2 elements (prev and curr)
newPrev = curr
curr += prev
return recur_fibo3(n-1, curr, newPrev) # recursive call
else:
return curr
它与n
的增加呈线性关系,但比正常循环慢。
此外,您还可以注意到,两个递归函数(经典函数和上一个函数)都没有存储整个序列供您返回。您的循环函数可以执行此操作,但是如果您只想检索序列中的第n个值,则可以编写一个更快的函数,如下所示:
def my_fibo2(n):
prev1 = 0
prev2 = 1
for _ in range(n-2):
curr = prev1 + prev2
prev2 = prev1
prev1 = curr
return curr
使用%timeit
来衡量执行时间,我们可以看到哪个更快。但是它们在正常情况下都足够快,因为您只需要计算一次长序列并将结果存储起来以备后用...:)
Time to return the 100th element in Fibonacci series
my_fibo(100)
10.4 µs ± 990 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
my_fibo2(100)
5.13 µs ± 1.68 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
recur_fibo3(100)
14.3 µs ± 2.51 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
============================================
Time to return the 1000th element in Fibonacci series
my_fibo(1000)
122 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
my_fibo2(1000)
82.4 µs ± 17.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
recur_fibo3(1000)
207 µs ± 19.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)