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

相比使用缓存优化,算法实现更加复杂,但是所小号的内存更小。

posted @ 2022-03-24 17:42  酷酷的排球  阅读(446)  评论(0编辑  收藏  举报