使用缓存方式优化递归函数与lru_cache
一.递归函数的弊端
递归函数虽然编写时用很少的代码完成了庞大的功能,但是它的弊端确实非常明显的,那就是时间与空间的消耗。
用一个斐波那契数列来举例
import time
#@lru_cache(20)
def fibonacci(n):
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
t1 = time.time()
print(fibonacci(35))
t2 = time.time()
print(t2 - t1) # 4.007285118103027
t1 = time.time()
print(fibonacci(36))
t2 = time.time()
print(t2 - t1) # 6.479698419570923
前面输入的数较小,所以算的还算很快,但输入到35、36来测试时已经要花上好几秒来计算了,而且36比35计算时间多了两秒多,可想而知数据再增大后消耗的时间增加的是越来越大的,因为这个递归函数的复杂性是O(2**n)
我们想一下这个函数递归的原理,流程,发现一个问题,计算fibonacci(35)的时候,是计算fibonacci(34)+fibonacci(33)的和,计算fibonacci(34)时,是计算的fibonacci(33)+fibonacci(32)的和,问题出现了,fibonacci(33)需要计算两次,那不是重复了嘛,我们继续递归向下拆分发现,几乎所有的递归函数拆分为两个函数的和时都会有重复计算,就想下面这个图:

以fibonacci(5)举例,这个图里面有一大部分的数字是重复的,也就是说执行了很多的重复的函数,这使我们产生了一个想法,既然重复执行了,那我让它直接返回之前执行时的返回值不就行了,至于之前执行时的返回值,给他存起来不就好了吗,这就用到了我们下面要说的缓存思想
二.用缓存优化递归函数
我们定义一个装饰器来做函数的缓存
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)
t1 = time.time()
print(fibonacci(35))
t2 = time.time()
print(t2 - t1) # 0
t1 = time.time()
print(fibonacci(36))
t2 = time.time()
print(t2 - t1) # 0
当使用了缓存的方式后,发现计算所用的时间已经接近0,我们把数再改大一点
t1 = time.time()
print(fibonacci(300))
t2 = time.time()
print(t2 - t1) # 0.001026153564453125
t1 = time.time()
print(fibonacci(301))
t2 = time.time()
print(t2 - t1) # 0.0
这也太厉害了,当把数增大到300时,花费的时间才是0.001秒,而且t2的计算结果为0也证明了的确装饰器中缓存了数据,计算fibonacci(301)可直接从缓存中拿fibonacci(300)和fibonacci(299),我们用图来更清晰的解释

图中用虚线所指的结点都不需要重新计算了,只计算了不重复的数字,也就是意味着复杂度从O(2**n)降到了O(n)
这种缓存的思想,给我们的优化带来了巨大的收益
三.lru_cache装饰器
上面的装饰器是我们自己写的,但它不适用与其他函数,比如有多个参数的函数,但是python标准库为我们提供了一个非常方便的装饰器来进行缓存
它是functools模块中的lru_cache(maxsize,typed)
通过其名就能让我们了解它,它是通过lru算法来进行缓存内容的淘汰,
maxsize参数设置缓存内存占用上限,其值应当设为2的幂,值为None时表示没有上限
typed参数设置表示不同参数类型的调用是否分别缓存,这个参数的意思是如果设置为True,那么fibonacci(5)和fibonacci(5.0)将分别缓存,不存为一个。
lru_cache的使用只需要将上面我们自定义的装饰器替换为 lru_cache(None,False)即可。
参考《python高级编程(第2版)》
使用缓存方式优化递归函数与lru_cache的更多相关文章
- NET Core静态文件的缓存方式
NET Core静态文件的缓存方式 阅读目录 一.前言 二.StaticFileMiddleware 三.ASP.NET Core与CDN? 四.写在最后 回到目录 一.前言 我们在优化Web服务的时 ...
- [转]NET Core静态文件的缓存方式
本文转自:https://www.cnblogs.com/Leo_wl/p/6059349.html 阅读目录 NET Core静态文件的缓存方式 一.前言 二.StaticFileMiddlewar ...
- MySQL缓存参数优化(转)
MySQL 数据库性能优化之缓存参数优化 数据库属于 IO 密集型的应用程序,其主要职责就是数据的管理及存储工作.而我们知道,从内存中读取一个数据库的时间是微秒级别,而从一块普通硬盘上读取一个IO是在 ...
- Windows系统虚拟内存文件和休眠缓存大小优化
虚拟内存的大小设置 虚拟内存的文件 pagefile.sys 一般在系统盘的根目录下,默认情况下会比较大.下面给出缩小设置方式. 我的电脑(鼠标右键)--属性--高级系统设置--切换到“高级”选项卡- ...
- MySQL 数据库性能优化之缓存参数优化
在平时被问及最多的问题就是关于 MySQL 数据库性能优化方面的问题,所以最近打算写一个MySQL数据库性能优化方面的系列文章,希望对初中级 MySQL DBA 以及其他对 MySQL 性能优化感兴趣 ...
- MySQL分页优化中的“INNER JOIN方式优化分页算法”到底在什么情况下会生效?
本文出处:http://www.cnblogs.com/wy123/p/7003157.html 最近无意间看到一个MySQL分页优化的测试案例,并没有非常具体地说明测试场景的情况下,给出了一种经典的 ...
- CacheConcurrencyStrategy五种缓存方式
CacheConcurrencyStrategy有五种缓存方式: CacheConcurrencyStrategy.NONE,不适用,默认 CacheConcurrencyStrategy.REA ...
- lodash源码分析之缓存方式的选择
每个人心里都有一团火,路过的人只看到烟. --<至爱梵高·星空之谜> 本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitb ...
- Django中提供的6种缓存方式
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用: 缓存,缓存将一个某个views的返回值保存至内存或者memcache中, ...
随机推荐
- linux系统中的文件权限详解(转)
一.在<Linux系统中如何查看文件属性>中介绍了通过ls指令来查看文件的属性,具体如下: [命令] letuknowit@ubuntu:/$ cd /tmp letuknowit@ubu ...
- Java中IO流中的装饰设计模式(BufferReader的原理)
本文粗略的介绍下JavaIO的整体框架,重在解释BufferReader/BufferWriter的演变过程和原理(对应的设计模式) 一.JavaIO的简介 流按操作数据分为两种:字节流与字符流. 流 ...
- Spring Boot 集成 Swagger2 与配置 OAuth2.0 授权
Spring Boot 集成 Swagger2 很简单,由于接口采用了OAuth2.0 & JWT 协议做了安全验证,使用过程中也遇到了很多小的问题,多次尝试下述配置可以正常使用. Maven ...
- JAR(Spring Boot)应用的后台运行配置
酱油一篇,整理一下关于Spring Boot后台运行的一些配置方式.在介绍后台运行配置之前,我们先回顾一下Spring Boot应用的几种运行方式: 运行Spring Boot的应用主类 使用Mave ...
- Java工程师学习指南 完结篇
Java工程师学习指南 完结篇 先声明一点,文章里面不会详细到每一步怎么操作,只会提供大致的思路和方向,给大家以启发,如果真的要一步一步指导操作的话,那至少需要一本书的厚度啦. 因为笔者还只是一名在校 ...
- 从零开始学 Web 之 ES6(六)ES6基础语法四
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- kubeadm部署kubernetes-1.12.0 HA集群-ipvs
一.概述 主要介绍搭建流程及使用注意事项,如果线上使用的话,请务必做好相关测试及压测. 1.基础环境准备 系统:ubuntu TLS 16.04 5台 docker-ce:17.06.2 kubea ...
- MySQL 事务机制
事务处理是保证数据安全的重要机制,事务有四个重要属性 ,根据它们的英文名称可以记为ACID: 原子性(Atomic): 事务操作是不可分割的; 事务只存在已执行和未执行两种状态,不存在只执行了部分指令 ...
- LinQ是什么?
•LINQ(发音:Link)是语言级集成查询(Language INtegrated Query) •LINQ是一种用来进行数据访问的编程模型,使得.NET语言可以直接支持数据查询 •LINQ的目标是 ...
- 浅谈c#的三个高级参数ref out 和Params
c#的三个高级参数ref out 和Params 前言:在我们学习c#基础的时候,我们会学习到c#的三个高级的参数,分别是out .ref 和Params,在这里我们来分别的讲解一下,在这里的我们先不 ...