遞歸
有人說,天才使用遞歸,因爲遞歸大大的減少了程序員的代碼量,而且能方便程序員實現許多的功能,但從內存等角度上,比如數據結構裏提到的空間複雜度和時間複雜度來說,遞歸算法很多情況下的時間複雜度往往是很大的,並稱不上一個好的算法,但是遞歸在某些方面,卻又有別的方式無法媲美的優點,今天舉三個例子,來溫習前幾天學習的遞歸。
1. 階乘的兩種實現:
#非遞歸:
def jiecheng(n):
y=1
for i in range(1,n+1): #range值取不到stop值本身,故加一
y*=i
return y
#遞歸實現:
#提前在程序開頭import sys
sys.setrecursionlimit(100) #設置最大遞歸層數爲100 是一個python的保護措施
def jiecheng2(x):
if x==0: #0的階乘爲1
y=1
else:
y=x*jiecheng2(x-1)
return y
下面用求6000的階乘對比一下兩種算法的優劣:
非遞歸:
圖片僅爲結果的一小部分,但是結果僅在一眨眼的情況下就運行出來了
遞歸:
可以看到,這裏遞歸6000層,超過了我開頭設置的100層,所以我們把遞歸允許層數設置大一點,再來嘗試一下:
import sys
sys.setrecursionlimit(10000) #設置最大遞歸層數爲10000
Process finished with exit code -1073741571 (0xC00000FD)
查看問題的根源在於遞歸導致的棧溢出
解決辦法可參考:誰說Python不能尾遞歸優化
2. 斐波那契數列的兩種實現:
#斐波那契數列 F(n) = 1(n=1,2) ; F(n-1)+F(n-2)
# 遞歸實現
def fibonacci(n):
if(n<1):
print('請重新輸入!不可小於1!')
y=-1
elif(n==1):
y=1
elif n==2:
y=1
else:
y=fibbonacci(n-1)+fibbonacci(n-2)
return y
n=int(input('請輸入幾個月:\n'))
print('遞歸', n, ' 個月後共有:', fibonacci(n), ' 對兔子')
# 非遞歸實現
def fab(n):
if(n<1):
print('請重新輸入!不可小於1!')
y=-1
elif(n==1):
y=1
elif n==2:
y=1
else:
n1=1
n2=1
n3=1
while(n-2>0):
n3=n2+n1
n1=n2
n2=n3
n-=1
y=n3
return y
print('非遞歸:20個月後共有:', fab(20), ' 對兔子')
對於斐波那契數列的遞歸和非遞歸算法進行測試,仍然是非遞歸比較優越。
但是這麼說我們的遞歸方法,除了減少程序的代碼量,就真的別無用處了嗎?當然不是!
請看我們的著名的漢諾塔算法:
3. 漢諾塔的遞歸實現:
遞歸的正確使用 – 漢諾塔
第一個參數是起始柱,第二個參數是過渡柱,第三個參數是終點柱
def hanoi(n,x,y,z):
if n==1:
print(x, '-->', z)
else:
hanoi(n - 1, x, z, y) #將前n-1從x移動到y上
print(x,'-->',z) #將最一個盤子從x移動到z上
hanoi(n - 1, y, x, z) #將前n-1從y移動到z上
# 注意以上兩次遞歸調用漢諾塔算法,起始柱,過渡柱,目標柱的變化!
n=int(input('請輸入漢諾塔的層數:\n'))
hanoi(n,'x','y','z')
遞歸大大的減少了程序員的代碼量,而且能方便程序員實現許多的功能,但是也比較喫內存喫時間,要在內存,時間和算法之間尋求一個平衡點,遞歸的用法還需斟酌再三,但我認爲不一定是天才纔可用遞歸,相信我們多學多練,也能找到那個平衡點的!