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分析git log日志示例
Feb 27 Python
python自动安装pip
Apr 24 Python
Python基础学习之常见的内建函数整理
Sep 06 Python
如何用Python实现简单的Markdown转换器
Jul 16 Python
tensorflow saver 保存和恢复指定 tensor的实例讲解
Jul 26 Python
关于pytorch中网络loss传播和参数更新的理解
Aug 20 Python
OpenCV+face++实现实时人脸识别解锁功能
Aug 28 Python
python使用opencv在Windows下调用摄像头实现解析
Nov 26 Python
django3.02模板中的超链接配置实例代码
Feb 04 Python
Matplotlib使用Cursor实现UI定位的示例代码
Mar 12 Python
python 实现分组求和与分组累加求和代码
May 18 Python
Python基础之字符串格式化详解
Apr 21 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
DC的38部超级英雄动画电影
2020/03/03 欧美动漫
也谈截取首页新闻 - 范例
2006/10/09 PHP
分享PHP入门的学习方法
2007/01/02 PHP
PHP面向对象——访问修饰符介绍
2012/11/08 PHP
php检测useragent版本示例
2014/03/24 PHP
php实现等比例压缩图片
2018/07/26 PHP
php查看一个变量的占用内存的实例代码
2020/03/29 PHP
Javascript 继承机制的实现
2009/08/12 Javascript
jQuery源码解读之addClass()方法分析
2015/02/20 Javascript
vue双向数据绑定知识点总结
2018/04/18 Javascript
React手稿之 React-Saga的详解
2018/11/12 Javascript
vue+iview动态渲染表格详解
2019/03/19 Javascript
js 根据对象数组中的属性进行排序实现代码
2019/09/12 Javascript
Openlayers3实现车辆轨迹回放功能
2020/09/29 Javascript
js实现石头剪刀布游戏
2020/10/11 Javascript
Python多层嵌套list的递归处理方法(推荐)
2016/06/08 Python
完美解决Python 2.7不能正常使用pip install的问题
2018/06/12 Python
Python之list对应元素求和的方法
2018/06/28 Python
Python图像处理之直线和曲线的拟合与绘制【curve_fit()应用】
2018/12/26 Python
python hough变换检测直线的实现方法
2019/07/12 Python
Python matplotlib画曲线例题解析
2020/02/07 Python
Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解
2020/03/30 Python
Python logging模块原理解析及应用
2020/08/13 Python
CSS3基础(RGBa、text-shadow、box-shadow、border-radius)
2012/11/13 HTML / CSS
浏览器实现移动端高性能css3动画(开启gpu加速)
2013/12/23 HTML / CSS
伦敦一家西班牙童装精品店:La Coqueta
2018/02/02 全球购物
九年级体育教学反思
2014/01/23 职场文书
大龄毕业生求职别忘职业规划
2014/03/11 职场文书
个人安全生产承诺书
2014/05/22 职场文书
小学生环保标语
2014/06/13 职场文书
2014年党员整改措施
2014/10/24 职场文书
2015年三好一满意工作总结
2015/07/24 职场文书
科级干部培训心得体会
2016/01/06 职场文书
2016年第十四个公民道德宣传日活动总
2016/04/01 职场文书
从贫穷到富有,是知识技能和学习力的差别
2019/08/20 职场文书
vue-cli3.0修改打包后的文件名和文件地址,打包后本地运行报错解决
2022/04/06 Vue.js