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网络编程学习笔记(九):数据库客户端 DB-API
Jun 09 Python
python之wxPython应用实例
Sep 28 Python
python通过apply使用元祖和列表调用函数实例
May 26 Python
python数据结构链表之单向链表(实例讲解)
Jul 25 Python
Python socket套接字实现C/S模式远程命令执行功能案例
Jul 06 Python
详解Python 函数如何重载?
Apr 23 Python
简单了解python字符串前面加r,u的含义
Dec 26 Python
dpn网络的pytorch实现方式
Jan 14 Python
Python如何通过百度翻译API实现翻译功能
Apr 02 Python
解决flask接口返回的内容中文乱码的问题
Apr 03 Python
Python 多线程共享变量的实现示例
Apr 17 Python
在 Python 中使用 MQTT的方法
Aug 18 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函数解决SQL injection
2006/12/09 PHP
php jq jquery getJSON跨域提交数据完整版
2013/09/13 PHP
php实现的Curl封装类Curl.class.php用法实例分析
2015/09/25 PHP
PHP中PDO的事务处理分析
2016/04/07 PHP
php获取今日开始时间和结束时间的方法
2017/02/27 PHP
PHP应用跨时区功能的实现方法
2019/03/21 PHP
javascript数组去重的六种方法汇总
2015/08/16 Javascript
理解JavaScript中worker事件api
2015/12/25 Javascript
从重置input file标签中看jQuery的 .val() 和 .attr(“value”) 区别
2016/06/12 Javascript
jQuery实现的placeholder效果完整实例
2016/08/02 Javascript
JavaScript中闭包之浅析解读(必看篇)
2016/08/25 Javascript
微信小程序网络请求wx.request详解及实例
2017/05/18 Javascript
基于pako.js实现gzip的压缩和解压功能示例
2017/06/13 Javascript
原生JS控制多个滚动条同步跟随滚动效果
2017/12/22 Javascript
JavaScript数组去重算法实例小结
2018/05/07 Javascript
vue自定义指令和动态路由实现权限控制
2020/08/28 Javascript
详谈python3中用for循环删除列表中元素的坑
2018/04/19 Python
tensorflow实现简单的卷积神经网络
2018/05/24 Python
删除DataFrame中值全为NaN或者包含有NaN的列或行方法
2018/11/06 Python
python调用函数、类和文件操作简单实例总结
2019/11/29 Python
基于Python爬取搜狐证券股票过程解析
2020/11/18 Python
TensorFlow低版本代码自动升级为1.0版本
2021/02/20 Python
HTML+CSS3模拟心的跳动实例代码
2017/09/05 HTML / CSS
HTML5本地存储和本地数据库实例详解
2017/09/05 HTML / CSS
美国顶尖折扣时尚购物网:Bluefly
2016/08/28 全球购物
英国女士家居服网站:hush
2017/08/09 全球购物
会计师事务所审计实习自我鉴定
2013/09/20 职场文书
智能电子应届生求职信
2013/11/10 职场文书
护理助产毕业生的求职信
2014/03/02 职场文书
青春演讲稿范文
2014/05/08 职场文书
员工安全承诺书
2014/05/22 职场文书
公安机关纪律作风整顿个人剖析材料材料
2014/10/10 职场文书
考试没考好检讨书(精选篇)
2014/11/16 职场文书
办公室文员岗位职责
2015/02/04 职场文书
你需要掌握的20个Python常用技巧
2022/02/28 Python
Docker安装MySql8并远程访问的实现
2022/07/07 Servers