斐波那契数列的五种求法与时间复杂度测算

斐波那契数列的四种求法与时间复杂度测算

定义:什么是斐波那契数列?

斐波那契数列

了解了斐波那契数列的特性,又该如何求解呢?不同方法所消耗的时间又是多少呢?接下来就做详细解析!

方法一: 普通循环法计算

%%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])



你可能感兴趣的:(Python学习历程,python,学习)