1. 程式人生 > >神奇的遞迴!一文讀懂函式遞迴(python實現)

神奇的遞迴!一文讀懂函式遞迴(python實現)

遞迴是指函式在定義中呼叫函式自身的方式,是數學歸納法思維的程式設計體現

是不是有點暈,來看例1:

在上式對階乘的定義中,計算n!需要知道(n-1)!,計算(n-1)!需要知道(n-2)!......以此類推,一直到計算2!需要知道1!,而從上面的定義中可知1!=1。那麼,2!就可以算出來了,3!, 4! ......這樣反向進行運算,最後就可以得到n!了。

從這個例子可以看出,遞迴有兩個關鍵特徵:

  1. 鏈條:計算過程存在鏈條,例1中的鏈條是n! = n(n-1)!
  2. 基例:存在一個或多個不需要再次遞迴的基例,例1的基例是1! = 1
def fact(n):
    if n == 0:
        return 1  # 基例
    else:
        rturn n*fact(n-1)  # 鏈條

從這段簡單的程式碼可以得到遞迴實現的一般套路:函式+分支語句

  • 遞迴本身是個函式,需要通過函式定義方式描述
  • 在函式內部,採用分支語句對輸入引數進行判斷,針對基例和鏈條分別編寫對應程式碼

在上述程式碼中,5!的階乘是怎麼實現的呢?遞迴先重複的複製自身並不斷代入更小的引數,就是下圖藍色箭頭所示;當輸入引數下降到基例n=0時,將基例的值代入再不斷往前推最終得到n=5的階乘,如下圖紅色箭頭所示。

再來看看其它幾個例子:

# 例2:字串反轉( 不準用s[::-1]哈哈哈)

def rvs(s):
    if s == '':
        return s  # 基例
    else:
        return rvs(s[1:]) + s[0]  # 鏈條
# 例3:斐波那契數列

def f(n):
    if n == 1 or n == 2:
        return 1
    else:
        return f(n - 1) + f(n - 2)

for i in range(1, 31):
    print(f(i))
# 例4:漢諾塔問題
# 問題描述:
# 將一摞從小到大堆放的圓盤從src(source)杆移動到dst(destination)杆
# 可以利用mid(middle)杆,一次移動一個,小的在上面

count = 0  # 移動次數

def hanoi(n, src, dst, mid):  # n 圓盤數量;src 原杆; dst 目標杆; mid 中間杆
    global count  # 全域性變數宣告
    if n == 1:
        print('{}:{}->{}'.format(n, src, dst))
        count += 1
    else:  # 遞迴鏈條,呼叫了兩次自身函式
        hanoi(n-1, src, mid, dst)  # 把n-1個從src移動到mid
        print('{}:{}->{}'.format(n, src, dst))   # 把第n個圓盤從src移動到dst
        count += 1
        hanoi(n-1, mid, dst, src)  # 把mid上的n-1個圓盤移動到dst