漢諾塔和遞迴

Mysticbinary發表於2024-08-30

目錄
  • 需求背景、限制條件、化簡
  • 模擬盤子的移動步驟
  • 遞迴實現Code
  • 分析
  • 練習 1


需求背景、限制條件、化簡

漢諾塔就是一個由柱子和盤子組成的玩具,它有一些玩法上的限制,主要是規定了盤子移動有限制。
想理解到遞迴本質,漢諾塔是個不錯的載體。
怎麼體會?
在盤子移動的過程中。

# 盤子的數量:
#   通常,我們假設有 n 個盤子。盤子在初始時按從大到小的順序疊放在一個柱子上(源柱子)。
#   這裡採用數字表示盤子,數字大小表示盤子大小。

# 柱子數量:
#   總共有三個柱子:源柱子(起始柱子)、目標柱子(最終柱子) 和輔助柱子(中間柱子)。
#   這裡用a,b,c list模擬三個柱子
#   規定list只能從末尾取出

# 移動規則:
#   每次只能移動一個盤子。
#   任何時刻盤子只能放在一個柱子上。
#   大的盤子不能放在小的盤子上。

# 目標:
#   將所有的盤子從源柱子移動到目標柱子,且遵守上述規則。

總結:

  1. 瞭解了問題和目標;
  2. 將現實問題抽出,化簡,採用符號化方式去表達;

模擬盤子的移動步驟

先正常演示一次移動:

n = 1
a = [1]
b = []
c = []

a = []
b = []
c = [1]

n = 2
a = [2,1]
b = []
c = []

(1)
a = [2]
b = [1]
c = []

(2)
a = []
b = [1]
c = [2]

(3)
a = []
b = []
c = [2,1]

**

n = 3
a = [3,2,1]
b = []
c = [1]

(1)
a = [3,2]
b = []
c = [1]

(2)
a = [3]
b = [2]
c = [1]

(3)
a = [3]
b = [2,1]
c = []

(4)
a = []
b = [2,1]
c = [3]

(5)
a = [1]
b = [2]
c = [3]

(6)
a = [1]
b = []
c = [3,2]

(7)
a = []
b = []
c = [3,2,1]


上面正向的思考,n約大,步驟越多,越來越亂, 抓不住重點。
必須得以反向思維思考:

n = 3
a = [3]
b = [2,1]
c = []

a = []
b = [2,1]
c = [3]

**

n = 4
a = [4]
b = [3,2,1]
c = []

a = []
b = [3,2,1]
c = [4]

**
n = 5
a = [5]
b = [4,3,2,1]
c = []

a = []
b = [4,3,2,1]
c = [5]
......

簡單的抓到一些固定特徵:

  • 不管n是幾,都是a到c.

  • 如果n>1,就把n-1的盤子都放到輔助柱子,n 直接去 c。(這個也是劃分出來的一個子問題)

遞迴實現Code

def hanoi(n: int, a: list, b: list, c: list):
    print("sub State:", a, b, c)
    if n == 1:
        # 基準情況
        c.append(a.pop())
        print("State 1:", a, b, c)
    else:
        # 遞迴情況
        hanoi(n - 1, a, c, b)

        c.append(a.pop())
        print("State 2:", a, b, c)

        hanoi(n - 1, b, a, c)


a = [3, 2, 1]
b = []
c = []

print("Initial state:", a, b, c)
hanoi(len(a), a, b, c)
print("Final state:", a, b, c)

核心:
在遞迴呼叫中,柱子的角色(源、目標、輔助)不斷交換。這種角色交換保證了每一步都能正確地完成盤子的移動。

out:

Initial state: [3, 2, 1] [] []
sub State: [3, 2, 1] [] []
sub State: [3, 2, 1] [] []
sub State: [3, 2, 1] [] []
State 1: [3, 2] [] [1]
State 2: [3] [1] [2]
sub State: [1] [3] [2]
State 1: [] [3] [2, 1]
State 2: [] [2, 1] [3]
sub State: [2, 1] [] [3]
sub State: [2, 1] [3] []
State 1: [2] [3] [1]
State 2: [] [1] [3, 2]
sub State: [1] [] [3, 2]
State 1: [] [] [3, 2, 1]
Final state: [] [] [3, 2, 1]

分析

採用視覺化方式觀察:
https://pythontutor.com/

看完的的一些體會:

  • 函式會巢狀函式, 多個巢狀函式之間一起組成一個整體的函式;
    image

  • 遞迴透過傳遞下去的變數,在 之間的改變,能達到一個很炸裂的效果;

  • 遞迴的層下沉,一般使用一個int變數控制。

練習 1

你有一個儲存員工資訊的資料結構,它包含了員工唯一的 id ,重要度和直系下屬的 id 。

給定一個員工陣列 employees,其中:

  • employees[i].id 是第 i 個員工的 ID。
  • employees[i].importance 是第 i 個員工的重要度。
  • employees[i].subordinates 是第 i 名員工的直接下屬的 ID 列表。

給定一個整數 id 表示一個員工的 ID,返回這個員工和他所有下屬的重要度的 總和。

image

# Definition for Employee.
class Employee:
    def __init__(self, id: int, importance: int, subordinates: List[int]):
        self.id = id
        self.importance = importance
        self.subordinates = subordinates

相關文章