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解决鸡兔同笼问题的方法
Dec 20 Python
python实现的用于搜索文件并进行内容替换的类实例
Jun 28 Python
深入解析Python中的集合类型操作符
Aug 19 Python
Python日期的加减等操作的示例
Aug 15 Python
浅析python中numpy包中的argsort函数的使用
Aug 30 Python
使用Python画股票的K线图的方法步骤
Jun 28 Python
pytorch实现onehot编码转为普通label标签
Jan 02 Python
从多个tfrecord文件中无限读取文件的例子
Feb 17 Python
Django中从mysql数据库中获取数据传到echarts方式
Apr 07 Python
python实现图片转换成素描和漫画格式
Aug 19 Python
如何基于matlab相机标定导出xml文件
Nov 02 Python
Python实现Matplotlib,Seaborn动态数据图
May 06 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
PHP开发中的错误收集,不定期更新。
2011/02/03 PHP
PHP下载远程图片的几种方法总结
2017/04/07 PHP
jQuery对象和DOM对象相互转化
2009/04/24 Javascript
JQuery 学习笔记 选择器之二
2009/07/23 Javascript
半角全角相互转换的js函数
2009/10/16 Javascript
Javascript 二维数组
2009/11/26 Javascript
js实现模拟计算器退格键删除文字效果的方法
2015/05/07 Javascript
JavaScript的jQuery库中function的存在和参数问题
2015/08/13 Javascript
纯js代码制作的网页时钟特效【附实例】
2016/03/30 Javascript
Node.js实现文件上传
2016/07/05 Javascript
jQuery实现简单的tab标签页效果
2016/09/12 Javascript
浅述节点的创建及常见功能的实现
2016/12/15 Javascript
简单理解js的冒泡排序
2016/12/19 Javascript
BootStrap TreeView使用实例详解
2017/11/01 Javascript
JavaScript中关于base64的一些事
2019/05/06 Javascript
微信小程序:报错(in promise) MiniProgramError
2020/10/30 Javascript
[02:43]DOTA2亚洲邀请赛场馆攻略——带你走进东方体育中心
2018/03/19 DOTA
Python实现的数据结构与算法之基本搜索详解
2015/04/22 Python
python3用PIL把图片转换为RGB图片的实例
2019/07/04 Python
安装PyInstaller失败问题解决
2019/12/14 Python
浅谈django 重载str 方法
2020/05/19 Python
python中upper是做什么用的
2020/07/20 Python
Scrapy爬虫文件批量运行的实现
2020/09/30 Python
PyCharm 2020.2.2 x64 下载并安装的详细教程
2020/10/15 Python
matplotlib之属性组合包(cycler)的使用
2021/02/24 Python
英国网上购买门:Direct Doors
2018/06/07 全球购物
英国领先的在线鱼贩:The Fish Society
2020/08/12 全球购物
班主任工作年限证明
2014/01/12 职场文书
农业局学习党的群众路线教育实践活动心得体会
2014/03/07 职场文书
政府信息公开实施方案
2014/05/09 职场文书
高效课堂标语
2014/06/26 职场文书
工伤认定行政答辩状
2015/05/22 职场文书
婚宴新郎致辞
2015/07/28 职场文书
合理化建议书范文
2015/09/14 职场文书
高中优秀作文(范文)
2019/08/15 职场文书
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
2021/06/09 Vue.js