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基于scapy实现修改IP发送请求的方法示例
Jul 08 Python
Tornado Web Server框架编写简易Python服务器
Jul 28 Python
python调用自定义函数的实例操作
Jun 26 Python
详解python websocket获取实时数据的几种常见链接方式
Jul 01 Python
Django高级编程之自定义Field实现多语言
Jul 02 Python
python中append实例用法总结
Jul 30 Python
python opencv实现信用卡的数字识别
Jan 12 Python
python数据库操作mysql:pymysql、sqlalchemy常见用法详解
Mar 30 Python
python实现学生成绩测评系统
Jun 22 Python
tensorflow 大于某个值为1,小于为0的实例
Jun 30 Python
Python项目打包成二进制的方法
Dec 30 Python
python 视频下载神器(you-get)的具体使用
Jan 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简单实现无限分类树形列表的方法
2015/03/27 PHP
PHP中JSON的应用技巧
2015/10/10 PHP
php基于PDO实现功能强大的MYSQL封装类实例
2017/02/27 PHP
基于PHP-FPM进程池探秘
2017/10/17 PHP
PHP读取文件,解决中文乱码UTF-8的方法分析
2020/01/22 PHP
javascript globalStorage类代码
2009/06/04 Javascript
JS中Iframe之间传值及子页面与父页面应用
2013/03/11 Javascript
jQuery提交多个表单的小技巧
2014/07/27 Javascript
JavaScript整除运算函数ceil和floor的区别分析
2015/04/14 Javascript
详解AngularJS实现表单验证
2015/12/10 Javascript
seajs学习之模块的依赖加载及模块API的导出
2016/10/20 Javascript
在一个页面重复使用一个js函数的方法详解
2016/12/26 Javascript
nodejs中express入门和基础知识点学习
2018/09/13 NodeJs
关于微信小程序登录的那些事
2019/01/08 Javascript
详解Vue 全局变量,局部变量
2019/04/17 Javascript
Vue项目总结之webpack常规打包优化方案
2019/06/06 Javascript
解决layer.open后laydate失效的问题
2019/09/06 Javascript
vue实现公告栏文字上下滚动效果的示例代码
2020/06/16 Javascript
[53:10]2018DOTA2亚洲邀请赛 4.6 淘汰赛 VP vs VG 第一场
2018/04/11 DOTA
python进阶教程之循环对象
2014/08/30 Python
Windows平台Python连接sqlite3数据库的方法分析
2017/07/12 Python
python shell根据ip获取主机名代码示例
2017/11/25 Python
Python实现读取txt文件并画三维图简单代码示例
2017/12/09 Python
django将图片上传数据库后在前端显式的方法
2018/05/25 Python
PyTorch CNN实战之MNIST手写数字识别示例
2018/05/29 Python
Python3爬虫学习入门教程
2018/12/11 Python
django ManyToManyField多对多关系的实例详解
2019/08/09 Python
应用OpenCV和Python进行SIFT算法的实现详解
2019/08/21 Python
使用python-pptx包批量修改ppt格式的实现
2020/02/14 Python
CSS3样式linear-gradient的使用实例
2017/01/16 HTML / CSS
HTML5的video标签的浏览器兼容性增强方案分享
2016/05/19 HTML / CSS
建筑项目策划书
2014/01/13 职场文书
小学新教师培训方案
2014/02/03 职场文书
拒绝黄毒毒宣传标语
2014/06/26 职场文书
武夷山导游词
2015/02/03 职场文书
2019银行员工个人工作自我鉴定
2019/06/27 职场文书