python-递归优化--使用缓存的方式-优化函数与lru_cache
递归函数的弊端
递归函数虽然编写时,使用很少的代码完成了庞大的功能,弊端非常明显--时间和空间的消耗。
eg:
import time
def fibonacci(n):
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
if __name__ == '__main__':
t1 = time.time()
print(fibonacci(35))
t2 = time.time()
print(f"cost: {t2 - t1} s")
结果:
14930352
cost: 5.771588563919067 s
耗时大概在5s,当数据继续增大时,消耗的是时间将会继续增加,越来越大,因为这个递归函数的复杂性是O(2**n)。
递归原理图:
当n=5时,斐波拉切数列的原理图如下:
根据原理图可知,其中大部分的数字是重复的,也就是说执行了很多重复的函数。
优化:将计算过的数值,缓存起来,然后再次进行计算。
用缓存优化递归
eg:
定义一个递归装饰器来做函数的缓存。
import time
def cache_decorator(func):
cache_dict = {}
def decorator(arg):
try:
return cache_dict[arg]
except KeyError:
return cache_dict.setdefault(arg, func(arg))
return decorator
@cache_decorator
def fibonacci(n):
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
if __name__ == '__main__':
t1 = time.time()
print(fibonacci(35))
t2 = time.time()
print(f"cost: {t2 - t1} s")
结果:
14930352
cost: 0.0 s
使用缓存装饰器之后,函数执行时间,大幅度缩短。
虚线所指的节点则不需要重新计算,意味复杂度从O(2**n)降到了O(n)。
lru_cache 装饰器
上述优化器,不适用其他的函数,使用python标准库提供的装饰器来进行缓存(functools模块中lru_cache).
其是通过lru算法来进行缓存内容的淘汰。
参数:
maxsize: 设置缓存内存上限, 其值应当设置为2的n次幂, 值为None
表示,没有上限。
typed: 表示不同参数类型,是否分别缓存, True:分别缓存
import time
from functools import lru_cache
def cache_decorator(func):
cache_dict = {}
def decorator(arg):
try:
return cache_dict[arg]
except KeyError:
return cache_dict.setdefault(arg, func(arg))
return decorator
# @cache_decorator
@lru_cache(2**10, False)
def fibonacci(n):
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
if __name__ == '__main__':
t1 = time.time()
print(fibonacci(300))
t2 = time.time()
print(f"cost: {t2 - t1} s")
结果:
359579325206583560961765665172189099052367214309267232255589801
cost: 0.0009965896606445312 s
一般的递归优化
思路:
对递归的优化,使用循环来进行处理
【Note】:递归和循环在之间存在转化的关系
# 优化解法
def fibonacci2(n: int):
res = [0, 1]
if n < 2:
return res[0]
else:
fibn_misnus_one = 1
fibn_misnus_two = 0
fibn = 0
for i in range(2, n):
fibn = fibn_misnus_one + fibn_misnus_two
fibn_misnus_two = fibn_misnus_one
fibn_misnus_one = fibn
return fibn
相比使用缓存优化,算法实现更加复杂,但是所小号的内存更小。