一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - Python中使用裝飾器來優(yōu)化尾遞歸的示例

Python中使用裝飾器來優(yōu)化尾遞歸的示例

2020-08-28 09:24cangmean Python

這里我們用典型的斐波那契數(shù)列作為例子,來展示Python中使用裝飾器來優(yōu)化尾遞歸的示例,需要的朋友可以參考下

尾遞歸簡介
尾遞歸是函數(shù)返回最后一個操作是遞歸調(diào)用,則該函數(shù)是尾遞歸。
遞歸是線性的比如factorial函數(shù)每一次調(diào)用都會創(chuàng)建一個新的棧(last-in-first-out)通過不斷的壓棧,來創(chuàng)建遞歸, 很容易導致棧的溢出。而尾遞歸則使用當前棧通過數(shù)據(jù)覆蓋來優(yōu)化遞歸函數(shù)。
階乘函數(shù)factorial, 通過把計算值傳遞的方法完成了尾遞歸。但是python不支出編譯器優(yōu)化尾遞歸所以當遞歸多次的話還是會報錯(學習用)。

eg:

?
1
2
3
4
5
6
7
def factorial(n, x):
  if n == 0:
    return x
  else:
    return factorial(n-1, n*x)
 
print factorial(5, 1) # 120

尾遞歸優(yōu)化
這里用到了斐波那契數(shù)來作為例子.線性遞歸的算法由于太過一低效就被我們Pass掉了,我們先來看尾遞過方式的調(diào)用:

?
1
2
3
4
5
6
7
8
(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)

這段程序我們來測試一下,調(diào)用 Fib(1001)結(jié)果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> 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),結(jié)果,茶幾了,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.....
 
 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
 
>>>

好了,現(xiàn)在我們來尾遞歸優(yōu)化

我們給剛才的Fib函數(shù)增加一個Decorator,如下:

?
1
2
3
4
5
6
7
8
9
@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神奇的打破了調(diào)用棧的限制。

這下即使我們Fib(20000),也能在780ms跑出結(jié)果(780ms是以前博文提到那臺2000元的上網(wǎng)本跑出來的結(jié)果)

不賣關(guān)子了,下面我們來看看這段神奇的代碼: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

使用的方法前面已經(jīng)展示了,令我感到大開眼界的是,作者用了拋出異常然后自己捕獲的方式來打破調(diào)用棧的增長,簡直是太匪夷所思了。而且效率問題,和直接尾遞歸Fib相比大概造成了五倍的時間開銷。

最后很不可思議的,尾遞歸優(yōu)化的目的達成了。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲欧美国产另类 | 黑人巨荃大战乌克兰美女 | 水野朝阳厨房系列在线观看 | 日韩永久在线观看免费视频 | 冰漪丰满大乳人体图片欣赏 | 日本一区二区三区精品 | 我的奶头被客人吸的又肿又红 | 国产福利不卡一区二区三区 | 青青在线观看 | 精品午夜寂寞黄网站在线 | h动态图男女啪啪27报 | 无人在线视频高清免费播放 | 91嫩草国产在线观看免费 | 99精品久久精品一区二区 | 岛国a香蕉片不卡在线观看 荡女淫春2古装 | 色老板在线播放 | 亚洲免费视频一区二区三区 | 国产伦精品一区二区 | 成人免费播放 | 日韩香蕉网| 99精品国产自产在线观看 | 香蕉在线精品一区二区 | 毛片啪啪视频 | 日本久久影视 | 精品国产欧美一区二区三区成人 | 午夜特级毛片 | 4hu永久地域网名入口 | 成年美女黄网站色视频大全免费 | 草莓视频深夜释放 | 好涨好爽好大视频免费 | 俄罗斯处女 | 鬼吹灯之天星术免费观看 | 91传媒在线观看 | 精品国语对白精品自拍视 | 福利视频久久 | 精品播放| 亚洲无线一二三四区 | 午夜精品在线 | 日本69av| 久久久GOGO无码啪啪艺术 | 亚洲AV久久无码精品九九软件 |