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的自动化部署模块Fabric的安装及使用指南
Jan 19 Python
Python处理Excel文件实例代码
Jun 20 Python
Python实现导出数据生成excel报表的方法示例
Jul 12 Python
python画图--输出指定像素点的颜色值方法
Jul 03 Python
使用Matplotlib 绘制精美的数学图形例子
Dec 13 Python
Tensorflow 定义变量,函数,数值计算等名字的更新方式
Feb 10 Python
Python class的继承方法代码实例
Feb 14 Python
Python如何实现定时器功能
May 28 Python
通俗讲解python 装饰器
Sep 07 Python
Python基于argparse与ConfigParser库进行入参解析与ini parser
Feb 02 Python
python spilt()分隔字符串的实现示例
May 21 Python
Python图片处理之图片裁剪教程
May 27 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个人网站架设连环讲(二)
2006/10/09 PHP
PHP获取mysql数据表的字段名称和详细信息的方法
2014/09/27 PHP
PHP实现指定字段的多维数组排序函数分享
2015/03/09 PHP
php函数重载的替代方法--伪重载详解
2015/05/08 PHP
PHP页面间传递值和保持值的方法
2016/08/24 PHP
window.location和document.location的区别分析
2008/12/23 Javascript
Jquery 表单取值赋值的一些基本操作
2009/10/11 Javascript
js 创建书签小工具之理论
2011/02/25 Javascript
23个超流行的jQuery相册插件整理分享
2011/04/25 Javascript
点击弹出层外区域关闭弹出层jquery特效示例
2013/08/25 Javascript
js获取鼠标点击的位置实现思路及代码
2014/05/09 Javascript
javascript实现炫酷的拖动分页
2015/05/11 Javascript
javascript中基本类型和引用类型的区别分析
2015/05/12 Javascript
AngularJS 让人爱不释手的八种功能
2016/03/23 Javascript
深入浅析JavaScript的API设计原则
2016/06/14 Javascript
jquery加载单文件vue组件的方法
2017/06/20 jQuery
BootStrap模态框和select2合用时input无法获取焦点的解决方法
2017/09/01 Javascript
JavaScript时间戳与时间日期间相互转换
2017/12/11 Javascript
react+redux的升级版todoList的实现
2017/12/18 Javascript
python中zip()方法应用实例分析
2016/04/16 Python
Python中import导入上一级目录模块及循环import问题的解决
2016/06/04 Python
Python数据分析之双色球统计两个红和蓝球哪组合比例高的方法
2018/02/03 Python
python3.x实现发送邮件功能
2018/05/22 Python
Python实现DDos攻击实例详解
2019/02/02 Python
Python读取表格类型文件代码实例
2020/02/17 Python
Python爬虫自动化爬取b站实时弹幕实例方法
2021/01/26 Python
世界上最大的汽车共享网站:Zipcar
2017/01/14 全球购物
大学生求职简历的自我评价范文
2013/10/12 职场文书
绿色校园广播稿
2014/10/13 职场文书
保安辞职信范文
2015/02/28 职场文书
五星级酒店宣传口号
2015/12/25 职场文书
考教师资格证不要错过的4个最佳时机
2019/07/17 职场文书
Python基础知识学习之类的继承
2021/05/31 Python
MySQL系列之三 基础篇
2021/07/02 MySQL
SQL之各种join小结详细讲解
2021/08/04 MySQL
Python利用FlashText算法实现替换字符串
2022/03/31 Python