1. 程式人生 > >python 學習彙總36:遞迴函式(尾遞迴)( tcy)

python 學習彙總36:遞迴函式(尾遞迴)( tcy)

遞迴函式(尾遞迴)                                                           2018/11/15 
用途:
遞迴函式常用於檢索大量資料,替代for迴圈。

1.遞迴深度設定:

sys.getrecursionlimit() #返回當前最多的遞迴深度預設3000 #ipython
sys.setrecursionlimit(1000) #設定遞迴深度 
注意:
       不能用在生成器函式和協程中
    
       函數語言程式設計並未通過 while 或者 for 宣告提供迭代功能,同樣也未提供狀態更新。
       任何可迭代的程式碼都可以轉換為可遞迴的程式碼 
2.例項1:定義階乘函式factorial ;遞迴實現
def factorial(n):
if n<=1:
return 1
else:
return n * factorial(n-1)

 

例項2:定義階乘factorial lambda表示式;遞迴實現
factorial1=(lambda n: 1 if n <= 1 else n * factorial1(n-1))#執行時間稍快於上面
測試 :
sys.setrecursionlimit(3000)#若設定3000,下面n<=2999
a=factorial(1) #1
print(a)
a=factorial(5) #120
print(a)
a=factorial(6) #720
print(a)
例項3:定義斐波那契數;遞迴實現
def fibonacci(n):
if n==1 or n==2:
return 1
return fibonacci(n-1)+fibonacci(n-2)

 

例項4:定義斐波那契數;lambda遞迴實現
fibonacci = (lambda x , x1=1,x2=0:
x2 if x == 0 else fibonacci (x - 1, x1 + x2, x1))

 

測試 :
a=fibonacci(1) #1
print(a)
a=fibonacci(5) #5
print(a)
a=fibonacci(6) #8
print(a)

 

3.尾遞迴
尾遞迴基於函式的尾呼叫, 每一級呼叫直接返回函式的返回值更新呼叫棧,而不用建立新的呼叫棧, 
類似迭代的實現,時間和空間上均優化了一般遞迴! 
python不支援尾遞迴。解決辦法先定義一個修飾符,然後在定義尾遞迴函式。

參考:
http://www.open-open.com/lib/view/open1480494663229.html 

尾遞迴定義例項:

階乘例項函式修改為支援尾遞迴,不需要在設定遞迴深度
如果無修飾符那麼n的能用資料範圍為2899(假設你設定遞迴深度為3000的話)
@tail_call_optimized #修飾符函式在下面定義
def factorial(n,acc=1):#階乘
if n==0:
return acc
else:
return n*factorial(n-1,n*acc) 

測試:

def factorial(n):#普通階乘遞迴定義,用於測試比對
if n<=1:
return 1
else:
return n * factorial(n-1)
n=2999
for i in range(n):
            if factorial(i)==factorial1(i):
            # print('ok')
            pass
        else:
            print('ng')
print('end') 
4.備註:
@tail_call_optimized的定義 :
4.1.異常類定義
class TailRecurseException(Exception):
def __init__(self, args, kwargs):
self.args = args
self.kwargs = kwargs

 

4.2.尾遞迴修飾符函式 :

def tail_call_optimized(g):
"""
這個函式用尾呼叫優化來修飾函式。它通過丟擲一個異常(如果是它自己的祖父母)來實現這一點,
並捕獲這樣的異常來偽造尾呼叫優化。如果修飾函式在非尾上下文中遞迴,則此函式將失敗。
"""
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 as e:
args = e.args # 捕獲異常, 拿到引數, 退出被修飾函式的遞迴呼叫棧
kwargs = e.kwargs
func.__doc__ = g.__doc__
return func