定义:什么是斐波那契数列?
斐波那契数列
了解了斐波那契数列的特性,又该如何求解呢?不同方法所消耗的时间又是多少呢?接下来就做详细解析!
方法一: 普通循环法计算
%%timeit # 用于测算时间,将下面的函数重复执行多次,求出其平均值
a ,b= 0,1
for i in range(20):# 求斐波那契数列的前20项,如果求100以内的斐波那契数列只需修改判断语句即可
a,b=b,a+b
print(b) #
>>>2.57 µs ± 226 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
方法二: 使用递归的方法
%%timeit
def fib(n):
if n<3:
return 1
return fib(n-1)+fib(n-2)
#或者
def fib(n):
return 1 if n <3 else fib(n-1)+fib(n-2) #三元表达式写法
fib(20)
>>>3.4 ms ± 189 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
方法三: 递归与循环相结合
def fib(n,a=0,b=1):
a,b=b,a+b
if n ==1 :
return a
return fib(n-1,a,b)
>>>6.98 µs ± 96 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
方法四: 使用functools
中的lru_cache()
# 缓存记忆
import functools #导入functools 模块
@functools.lru_cache(maxsize = 100) #设定参数
%%timeit
def fib(n):
return 1 if n <3 else fib(n-1)+fib(n-2)
fib(20)
>>>3.16 ms ± 29.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
总结:
从上面的运行结果可以看出,不同的方法计算同一个范围内的斐波那契数列的时间是不相同的,而且差距很大(这里仅用前20个做了测试,读者可以加大测算范围).用循环的方法计算速度是最快的,递归的方法最慢.
其中:方法四与方法二的核心代码是一样的,不同的是方法四调用了functools
,该方法可以记住最近使用的内存,这样在计算时,如果使用到相同的函数,则立即返回该值,如求fib(30)时需要用到fib(29)与fib(28) .由于内存中已经存在了后者的数据,故只需读取即可,不需要在计算,因此可以大大节省运行时间.
测试如下:
#方法二
%%timeit
def fib(n):
return 1 if n <3 else fib(n-1)+fib(n-2)
fib(40)
1min 9s ± 8.08 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
#方法四
%%timeit
def finc():
import functools #导入functools 模块
@functools.lru_cache()
def fib(n):
return 1 if n <3 else fib(n-1)+fib(n-2)
fib(40)
finc()
42.9 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
# 当数据量一大,二者的计算时间就会有明显的差异,因此,在使用递归时,需要考虑程序的运行时间.
2019年情人节更新:
使用面向对象的方法进行求解,并增加迭代方法,返回容器的长度,支持索引
class Fib:
def __init__(self):
self.items = [0,1,1]
def __len__(self):
return len(self.items)
def __iter__(self):
return iter(self.items)
def __getitem__(self,index):
if index <0:
raise IndexError('not negative')
if index <len(self.items):
return self.items[index]
for i in range(len(self.items),index+1):# 与当前长度有关,下次就能用
self.items.append(self.items[i-1]+self.items[i-2])
return self.items[index]
def __call__(self,index):
return self[index] # 调用上面的__getitem__ # 相当于f[5]
def __repr__(self):
return "{}{}".format(self.__class__.__name__,self.items)# 这里返回的是所以值,要修改
f = Fib()
print(f(5))
print(f)
print(f(35))
print(f(20))
print(f(40))
print(f(5))
print(f[5])