Python中使用装饰器来优化尾递归的示例


Posted in Python onJune 18, 2016

尾递归简介
尾递归是函数返回最后一个操作是递归调用,则该函数是尾递归。
递归是线性的比如factorial函数每一次调用都会创建一个新的栈(last-in-first-out)通过不断的压栈,来创建递归, 很容易导致栈的溢出。而尾递归则使用当前栈通过数据覆盖来优化递归函数。
阶乘函数factorial, 通过把计算值传递的方法完成了尾递归。但是python不支出编译器优化尾递归所以当递归多次的话还是会报错(学习用)。

eg:

def factorial(n, x):
  if n == 0:
    return x
  else:
    return factorial(n-1, n*x)

print factorial(5, 1) # 120

尾递归优化
这里用到了斐波那契数来作为例子.线性递归的算法由于太过一低效就被我们Pass掉了,我们先来看尾递过方式的调用:

(n,b1=1,b2=1,c=3):
 if n<3:
  return 1
 else:
  if n==c:
   return b1+b2
  else:
   return Fib(n,b1=b2,b2=b1+b2,c=c+1)

这段程序我们来测试一下,调用 Fib(1001)结果:

>>> def Fib(n,b1=1,b2=1,c=3):

...  if n<3:

...   return 1

...  else:

...   if n==c:

...    return b1+b2

...   else:

...    return Fib(n,b1=b2,b2=b1+b2,c=c+1)

... 

>>> Fib(1001)

70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501L

>>>

如果我们用Fib(1002),结果,茶几了,如下:

.....

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

RuntimeError: maximum recursion depth exceeded

>>>

好了,现在我们来尾递归优化

我们给刚才的Fib函数增加一个Decorator,如下:

@tail_call_optimized
def Fib(n,b1=1,b2=1,c=3):
 if n<3:
  return 1
 else:
  if n==c:
   return b1+b2
  else:
   return Fib(n,b1=b2,b2=b1+b2,c=c+1)

 
恩,就是这个@tail_call_optimized的装饰器 ,这个装饰器使Python神奇的打破了调用栈的限制。

这下即使我们Fib(20000),也能在780ms跑出结果(780ms是以前博文提到那台2000元的上网本跑出来的结果)

不卖关子了,下面我们来看看这段神奇的代码: 

class TailRecurseException: 
 def __init__(self, args, kwargs): 
 self.args = args 
 self.kwargs = kwargs 
 
def tail_call_optimized(g): 
 """ 
 This function decorates a function with tail call 
 optimization. It does this by throwing an exception 
 if it is it's own grandparent, and catching such 
 exceptions to fake the tail call optimization. 
 
 This function fails if the decorated 
 function recurses in a non-tail context. 
 """ 
 def func(*args, **kwargs): 
 f = sys._getframe() 
 if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code: 
  raise TailRecurseException(args, kwargs) 
 else: 
  while 1: 
  try: 
   return g(*args, **kwargs) 
  except TailRecurseException, e: 
   args = e.args 
   kwargs = e.kwargs 
 func.__doc__ = g.__doc__ 
 return func

使用的方法前面已经展示了,令我感到大开眼界的是,作者用了抛出异常然后自己捕获的方式来打破调用栈的增长,简直是太匪夷所思了。而且效率问题,和直接尾递归Fib相比大概造成了五倍的时间开销。

最后很不可思议的,尾递归优化的目的达成了。

Python 相关文章推荐
深入理解Python中命名空间的查找规则LEGB
Aug 06 Python
Python探索之自定义实现线程池
Oct 27 Python
pandas通过loc生成新的列方法
Nov 28 Python
配置 Pycharm 默认 Test runner 的图文教程
Nov 30 Python
用python做游戏的细节详解
Jun 25 Python
Tornado实现多进程/多线程的HTTP服务详解
Jul 25 Python
Python实用库 PrettyTable 学习笔记
Aug 06 Python
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
Feb 07 Python
keras训练曲线,混淆矩阵,CNN层输出可视化实例
Jun 15 Python
如何验证python安装成功
Jul 06 Python
python基于pygame实现飞机大作战小游戏
Nov 19 Python
python 三种方法提取pdf中的图片
Feb 07 Python
举例讲解Python面向对象编程中类的继承
Jun 17 #Python
浅谈python为什么不需要三目运算符和switch
Jun 17 #Python
python运行时间的几种方法
Jun 17 #Python
从局部变量和全局变量开始全面解析Python中变量的作用域
Jun 16 #Python
实例讲解Python中global语句下全局变量的值的修改
Jun 16 #Python
最大K个数问题的Python版解法总结
Jun 16 #Python
Python中的多行注释文档编写风格汇总
Jun 16 #Python
You might like
Linux下进行MYSQL编程时插入中文乱码的解决方案
2007/03/15 PHP
php压缩多个CSS为一个css的代码并缓存
2011/04/21 PHP
Yii实现多数据库主从读写分离的方法
2014/12/29 PHP
帝国cms常用标签汇总
2015/07/06 PHP
WordPress中缩略图的使用以及相关技巧
2015/11/24 PHP
PHP面向对象五大原则之接口隔离原则(ISP)详解
2018/04/04 PHP
PHP匿名函数(闭包函数)详解
2019/03/22 PHP
jQuery实现文件上传进度条特效
2015/08/12 Javascript
js实现图片无缝滚动
2015/12/23 Javascript
jQuery Chart图表制作组件Highcharts用法详解
2016/06/01 Javascript
onmouseover事件和onmouseout事件全面理解
2016/08/15 Javascript
ligerUI---ListBox(列表框可移动的实例)
2017/11/28 Javascript
24个ES6方法解决JS实际开发问题(小结)
2020/05/31 Javascript
JavaScript中常用的3种弹出提示框(alert、confirm、prompt)
2020/11/10 Javascript
js中延迟加载和预加载的具体使用
2021/01/14 Javascript
[42:32]VP vs RNG 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
python数组过滤实现方法
2015/07/27 Python
Windows平台Python连接sqlite3数据库的方法分析
2017/07/12 Python
python生成器,可迭代对象,迭代器区别和联系
2018/02/04 Python
对python中的argv和argc使用详解
2018/12/15 Python
浅谈PyQt5中异步刷新UI和Python多线程总结
2019/12/13 Python
基于python3的socket聊天编程
2020/02/17 Python
浅谈Pycharm的项目文件名是红色的原因及解决方式
2020/06/01 Python
HTML5本地存储之Web Storage详解
2016/07/04 HTML / CSS
详解Html5 监听拦截Android返回键方法
2018/04/18 HTML / CSS
Groupon比利时官方网站:特卖和网上购物高达-70%
2019/08/09 全球购物
出纳岗位职责范本
2013/12/01 职场文书
《问银河》教学反思
2014/02/19 职场文书
护士个人自我鉴定
2014/03/24 职场文书
保密承诺书范文
2014/03/27 职场文书
保证书格式范文
2014/04/28 职场文书
2014年教学管理工作总结
2014/12/02 职场文书
女人创业励志语录,句句蕴含能量,激发你的潜能
2019/08/20 职场文书
Nginx域名转发https访问的实现
2021/03/31 Servers
python模块与C和C++动态库相互调用实现过程示例
2021/11/02 Python
Python 第三方库 openpyxl 的安装过程
2022/12/24 Python