functiils.lru_cache缩短递归时间

力扣上看到一道题:算法

假设你正在爬楼梯。须要 n 阶你才能到达楼顶。编程

每次你能够爬 1 或 2 个台阶。你有多少种不一样的方法能够爬到楼顶呢?缓存

注意:给定 n 是一个正整数。函数

 

  使用普通递归解决,超出时间限制:post

  添加lru_cache以后,缩短了时间性能

 

lru_cache的解释以下:测试

 

如今你已经看到了如何本身实现一个memoization函数,我会告诉你,你可使用Python的functools.lru_cache装饰器来得到相同的结果,以增长方便性。优化

我最喜欢Python的缘由之一就是它的语法的简洁和美丽与它的哲学的美丽和简单性并行不悖。Python被称做“内置电池(batteries included)”,这意味着Python捆绑了大量经常使用的库和模块,这些只须要一个import声明!spa

我发现functools.lru_cache是一个很好的例子。lru_cache装饰器是Python标准库实现的易于使用的记忆功能。一旦你认识到何时使用lru_cache,你只需几行代码就能够快速加快你的应用程序。blog

咱们再来看看咱们的斐波那契数列示例。这一次,我会告诉你如何使用functools.lru_cache装饰器添加记忆:

用functools.lru_cache实现Python的Memoization

请注意我给lru_cache传递的maxsize参数是同时来限制存储在缓存中的项目数量。

我再一次使用该timeit模块来运行一个简单的基准测试,以便了解这种优化对性能的影响:

用functools.lru_cache实现Python的Memoization

您可能想知道,为何咱们此次可以以更快的速度得到第一次运行的结果。第一次运行缓存不该该是 “冻结”的吗?

不一样的是,在这个例子中,我在函数定义的时候使用了@lru_cache装饰器。这意味着此次递归调用fibonacci也在缓存中查找。

经过@lru_cache装饰器装饰fibonacci函数,我基本上把它变成了一个动态编程解决方案,每一个子问题只须要存储一次子问题解决方案,并在下次尝试解决相同问题时从缓存中查找结果。

这只是一个例子——但我相信你开始可以看到使用memoization装饰器的美丽和强大,而且开始意识到实现一个动态算法可以带来多大的好处。

 

为何你应该喜欢 functools.lru_cache

通常来讲,由functools.lru_cache实现的Python的memoization比咱们的专用memoize函数更全面,就像你在CPython源代码中看到的同样。

例如,它提供了一个方便的功能,容许您使用cache_info方法检索缓存统计信息:

用functools.lru_cache实现Python的Memoization

再一次,正如你在CacheInfo输出中看到的那样,Python的lru_cache记住了递归调用fibonacci。当咱们查看memoized函数的缓存信息时,您会发现为何它在第一次运行时比咱们的版本更快——缓存命中了34次。

正如我以前所暗示的,functools.lru_cache还容许您使用maxsize参数限制缓存结果的数量。经过设置maxsize=None你能够强制缓存是无界的,我一般会反对这样作。

还有一个typed布尔参数能够设置为True告诉缓存,不一样类型的函数参数应该分开缓存。例如,fibonacci(35)和fibonacci(35.0)将被视为产生大相径庭结果的不一样调用。

另外一个有用的功能是能够随时使用cache_clear方法重置结果缓存:

用functools.lru_cache实现Python的Memoization

若是您想了解更多关于使用lru_cache装饰器的复杂信息,我建议您参考Python标准库文档。

总之,你永远不须要推出本身的记忆功能。Python的内置方法lru_cache是易于使用的,更全面和通过测试的。

 

缓存注意事项——什么是能够被记忆的?

理想状况下,您将要记忆肯定性的函数。

用functools.lru_cache实现Python的Memoization

这deterministic_adder是一个肯定性函数,由于它老是会为相同的一对参数返回相同的结果。例如,若是您将2和3传入该函数,它将始终返回5。

将此行为与如下非肯定性函数进行比较:

用functools.lru_cache实现Python的Memoization

这个函数是不肯定的,由于它对于一个给定的输入的输出会根据星期几而变化:若是你在星期一运行这个函数,缓存将在一周中的任何一天返回陈旧的数据。

通常来讲,我发现任何更新记录或返回随时间变化的信息的函数对于记忆都是很差的选择。

或者,正如Phil Karlton所说:

计算机科学只有两件难事:缓存失效和命名事物。

——Phil Karlton

 

Python中的记忆:快速总结

在这篇Python教程中,您看到了memoization如何经过基于提供给它的参数缓存输出来优化函数。

一旦你记忆一个函数,它将只为你调用的每一组参数计算一次输出。第一次调用以后的每次调用都将快速从缓存中检索出来。

您看到了如何从头开始编写本身的memoization装饰器,以及为何在生产代码中您可能想要用Python内置的lru_cache:

记忆是一种软件优化技术,它根据参数存储返回函数调用的结果。

若是你的代码符合某个标准,memoization能够是一个很好的方法来加快你的应用程序。

您能够从Python标准库中导入一个全面的memoization函数,functools模块中的lru_cache。