很多人都說遞迴,簡單來講就是函式自己叫自己,也有人說他很直觀好理解,但其實我當初理解起來挺困難的...
所以我們這裡先從數學的東西來開始理解,給定一個遞迴關係式 (Recurrence Relation):
那這東西怎麼看呢?
假設我現在想要知道 a3,那要怎麼得到?
我們可以先從 (2) 式開始看:
那我們就可以得到我們的 a3 = 5 了!
那在現在將我們的 an 換成 f(n)
一樣把它分解可以得到:
這裡的 f(n) 就很像我們的程式碼例如 python
的
def f(n):
return
那我們就可以把剛剛的這個變成程式碼。
def f(n):
# f(0) = 2
if n == 0:
return 2
# 下又叫了一次自己這個 f(n) 函式,所以前面會說自己叫自己
# f(n) = f(n-1) +1
return f(n-1) + 1
這就是一個簡單的遞迴 (Recursion)!
像前面這種只跟自己前一項有關的我們可以叫做 一階線性遞迴關係式 (First-Order Linear Recurrence Relation)。
那肯定也有 二階遞迴關係式 (Second-Order Linear Recurrence Relation) 對吧?
沒錯!
二階遞迴關係式就是自己跟前兩項有關,常見的例子像是費波那契數,也有人稱作黃金分割數列,關係式如下。
程式碼就可以寫成:
def f(n):
# f(0) = 0
if n == 0:
return 0
# f(1) = 1
if n == 1:
return 1
# f(n) = f(n-1) + f(n-2)
return f(n-1) + f(n-2)
河內塔是一個經典的遞迴問題,規則如下:
要怎麼以最少的步驟解決呢?
以下我們以 5 個盤子來解釋
開始時可能會想說第一個盤子要怎麼動呢?
但其實我們可以反過來想,最後一盤子要怎麼動呢?
所以,我們先不看其他盤子,只看最後一個,那肯定就是 A -> C。
再來加上一個盤子,變兩個盤子後要怎麼移動?
那這不就跟前面兩個盤子的例子一樣嗎?
我們先整理一些有用的資訊:
所以要把最後一個盤子從 A 移到 C,其餘的盤子一定要在 B。
那現在就出現了一個小問題,其餘的盤子怎麼移到 B?
因為其餘的盤子只有兩個,所以就是兩個盤子的移動法,只是將 B、C 交換。
現在解決了「其餘的盤子怎麼移到 B?」,我們就能完成「最後一個盤子從 A 移到 C」。
再出現另外一個小問題「其餘在 B 的盤子怎麼移到 C?」,這裡又出現了兩個盤子的解決法。
最後完成了三個盤子的移動!且解法為:
那其實可以跟前面解決三個盤子的一樣,除了最後一個的都是其餘的盤子
那我們就會發現其餘盤子都可以視為一個整體。
這個四個盤子的大問題,就會變成解決「兩次其餘的盤子」的跟「最後一個盤子」的小問題
解法為:
如果我們要知道 n 個盤子的最少的步驟就可以列出:
def hanoi(n):
if n == 1:
return 1
return 2 * hanoi(n - 1) + 1
如果我們想知道 n 個盤子在 A、B、C 柱的移動路徑就可以寫出:
def hanoi(n, source, target, auxiliary):
if n == 1:
print(f"{source} -> {target}")
return
# A -> B
hanoi(n-1, source, auxiliary, target)
# A -> C
hanoi(1, source, target, auxiliary)
# B -> C
hanoi(n-1, auxiliary, target, source)
# 將 3 個盤子從 A 移到 C
hanoi(3, 'A', 'C', 'B')
謝謝~