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 09 Python
python微信跳一跳系列之棋子定位像素遍历
Feb 26 Python
python中实现数组和列表读取一列的方法
Apr 03 Python
pytorch + visdom CNN处理自建图片数据集的方法
Jun 04 Python
python按时间排序目录下的文件实现方法
Oct 17 Python
Python代码实现删除一个list里面重复元素的方法
Apr 02 Python
Python数据类型之列表和元组的方法实例详解
Jul 08 Python
python selenium 查找隐藏元素 自动播放视频功能
Jul 24 Python
Django rest framework jwt的使用方法详解
Aug 08 Python
python操作openpyxl导出Excel 设置单元格格式及合并处理代码实例
Aug 27 Python
Python lxml模块的基本使用方法分析
Dec 21 Python
Python importlib模块重载使用方法详解
Oct 13 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
FCKeditor添加自定义按钮
2008/03/27 PHP
解析PHP多种序列化与反序列化的方法
2013/06/06 PHP
php递归使用示例(php递归函数)
2014/02/14 PHP
PHP上传Excel文件导入数据到MySQL数据库示例
2016/10/25 PHP
php框架CodeIgniter主从数据库配置方法分析
2018/05/25 PHP
7款风格新颖的jQuery/CSS3菜单导航分享
2013/04/23 Javascript
jqGrid日期格式的判断示例代码(开始日期与结束日期)
2013/11/08 Javascript
javascript中字符串的定义示例代码
2013/12/19 Javascript
深入探讨javascript函数式编程
2015/10/11 Javascript
ECMAScript6轮播图实践知识总结
2016/08/17 Javascript
原生js仿淘宝网商品放大镜效果
2017/02/28 Javascript
浅谈 Vue v-model指令的实现原理
2017/06/08 Javascript
vue中的模态对话框组件实现过程
2018/05/01 Javascript
一个Vue页面的内存泄露分析详解
2018/06/25 Javascript
微信小程序使用for循环动态渲染页面操作示例
2018/12/25 Javascript
Vue两个版本的区别和使用方法(更深层次了解)
2020/02/16 Javascript
vue封装自定义指令之动态显示title操作(溢出显示,不溢出不显示)
2020/11/12 Javascript
Vue过滤器,生命周期函数和vue-resource简单介绍
2021/01/12 Vue.js
python字典序问题实例
2014/09/26 Python
python2.7 mayavi 安装图文教程(推荐)
2017/06/22 Python
Pycharm代码无法复制,无法选中删除,无法编辑的解决方法
2018/10/22 Python
Python中的引用知识点总结
2019/05/20 Python
python 的 scapy库,实现网卡收发包的例子
2019/07/23 Python
Linux安装Python3如何和系统自带的Python2并存
2020/07/23 Python
CSS3圆角和渐变2种常用功能详解
2016/01/06 HTML / CSS
css3的过滤效果简单实例
2016/08/03 HTML / CSS
HTML5 Canvas概述
2009/08/26 HTML / CSS
Unix如何在一行中运行多个命令
2015/05/29 面试题
大专生简历的自我评价
2013/11/26 职场文书
护士思想汇报
2014/01/12 职场文书
考试不及格的检讨书
2014/01/22 职场文书
社区爱国卫生月活动总结
2014/06/30 职场文书
学习焦裕禄同志为人民服务思想汇报
2014/09/10 职场文书
2014离婚协议书范文(3篇)
2014/11/29 职场文书
教你用python控制安卓手机
2021/05/13 Python
漫画「请问您今天要来点兔子吗?」最新杂志彩页公开
2022/03/24 日漫