07.3 遞迴關係式#
本節目標#
- 理解遞迴數列(recurrence)的概念:用前面的項定義後面的項。
- 用 SymPy 的
rsolve()解線性遞迴關係式,求出閉式(closed form)。 - 推導 Fibonacci 數列的 Binet 公式,並用矩陣特徵值(連結 6.3 節)重新推導。
- 實驗:解出 Tower of Hanoi 的遞迴式。
一、遞迴數列的概念#
遞迴數列用「前一項(或前幾項)」來定義下一項。例如等差數列:
最有名的例子是 Fibonacci 數列:
遞迴式方便計算,但我們常想知道第 \(n\) 項的「直接公式」(閉式)。把遞迴式解成 \(a_n\) 的顯式表達式,就是解遞迴關係式。
先用 Python 一步步算 Fibonacci(數值方法):
def fib_iter(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
print([fib_iter(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
二、rsolve():解線性遞迴#
SymPy 的 rsolve(遞迴式, 函數, 初始條件) 能把遞迴式解成閉式。先看簡單的等比遞迴 \(a_{n+1} = 2 a_n\) , \(a_0 = 3\) :
from sympy import rsolve, symbols, Function, simplify, sqrt, Rational
a = symbols('a', integer=True, nonnegative=True)
f = Function('f')
sol = rsolve(f(a + 1) - 2*f(a), f(a), {f(0): 3})
print(sol) # 3*2**a
再看等差遞迴 \(a_{n+1} = a_n + 5\) , \(a_0 = 2\) :
print(rsolve(f(a + 1) - f(a) - 5, f(a), {f(0): 2})) # 5*a + 2
解 Fibonacci 遞迴:
F = rsolve(f(a + 2) - f(a + 1) - f(a), f(a), {f(0): 0, f(1): 1})
print(simplify(F))
# sqrt(5)*(1/2 + sqrt(5)/2)**a/5 - sqrt(5)*(1/2 - sqrt(5)/2)**a/5
三、Fibonacci 的閉式:Binet 公式#
上式正是著名的 Binet 公式:
其中 \(\frac{1+\sqrt{5}}{2}\) 就是黃金比例 \(\varphi \approx 1.618\) 。驗證它確實給出 Fibonacci 數:
phi = (1 + sqrt(5)) / 2
psi = (1 - sqrt(5)) / 2
F_binet = (phi**a - psi**a) / sqrt(5)
expected = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
for i, e in enumerate(expected):
assert simplify(F_binet.subs(a, i)) == e
print("Binet 公式驗證通過")
# 有趣的性質:相鄰兩項的比值趨近黃金比例
print((F_binet.subs(a, a + 1) / F_binet.subs(a, a)).limit(a, 3, '+')) # 會趨近 phi
四、用矩陣特徵值推 Fibonacci(連結 6.3)#
把遞迴 \(F_{n+2} = F_{n+1} + F_n\) 寫成矩陣形式:
反覆套用可得 \(\begin{pmatrix} F_{n+1} \\ F_n \end{pmatrix} = A^n \begin{pmatrix} 1 \\ 0 \end{pmatrix}\) 。矩陣的特徵值決定行為——這正是 6.3 節特徵值理論的應用:
from sympy import Matrix
A = Matrix([[1, 1], [1, 0]])
print(A.eigenvals())
# {1/2 + sqrt(5)/2: 1, 1/2 - sqrt(5)/2: 1}
# 特徵值就是 phi 與 psi!閉式必然是 c1*phi^n + c2*psi^n 的形式
print(A.eigenvects())
用矩陣冪直接驗證:
v = Matrix([1, 0]) # (F_1, F_0)
for i in range(2, 12):
Fn = (A**(i - 1) * v)[0]
print(i, Fn)
# 依序印出 1, 2, 3, 5, 8, 13, ...
rsolve 的使用細節:
- 遞迴式要寫成「式子 = 0」的形式,例如
f(n+2) - f(n+1) - f(n)。 - 初始條件用字典
{f(0): 0, f(1): 1},個數需與遞迴的「階數」相同(二階遞迴給兩個條件)。 - 函數符號要用
Function('f')定義,索引變數需宣告為整數。 rsolve也支援非齊次項(如 \(a_{n+1} = 2a_n + 1\) )與非線性某些型態,但線性齊次最可靠。
# 非齊次範例:a_{n+1} = 2*a_n + 1, a_0 = 0(河內塔的同型遞迴)
print(rsolve(f(a + 1) - 2*f(a) - 1, f(a), {f(0): 0})) # 2**a - 1
五、動手實驗:Tower of Hanoi 遞迴式解出#
河內塔規則: \(n\) 個盤子從柱 A 搬到柱 C,一次搬一個、小盤永遠在大盤上。搬 \(n\) 個盤子的最少步數 \(T_n\) 滿足:
直覺:先把上面 \(n-1\) 個搬到暫存柱( \(T_{n-1}\) 步),搬最大盤(1 步),再把 \(n-1\) 個疊回來( \(T_{n-1}\) 步)。
先用遞迴算出前幾項,猜測規律:
def hanoi(n):
if n == 1:
return 1
return 2 * hanoi(n - 1) + 1
print([hanoi(i) for i in range(1, 9)])
# [1, 3, 7, 15, 31, 63, 127, 255]
# 規律:2^n - 1
用 rsolve 正式解出:
T = rsolve(f(a + 1) - 2*f(a) - 1, f(a), {f(1): 1})
print(simplify(T)) # 2**a - 1
驗證閉式與遞迴一致:
for i in range(1, 12):
assert simplify(T.subs(a, i)) == hanoi(i)
print("河內塔閉式驗證通過")
再解另一個變體:若可以「一次搬兩個相鄰盤」(簡化規則),遞迴變成 \(T_n = 2 T_{n-1} - 1\) :
T2 = rsolve(f(a + 1) - 2*f(a) + 1, f(a), {f(1): 1})
print(simplify(T2)) # 1(常數解)
實驗 2:另一個經典遞迴——爬樓梯問題。一次可爬 1 階或 2 階,爬 \(n\) 階的方法數 \(S_n = S_{n-1} + S_{n-2}\) ,正是 Fibonacci:
S = rsolve(f(a + 2) - f(a + 1) - f(a), f(a), {f(1): 1, f(2): 2})
print([simplify(S.subs(a, i)) for i in range(1, 10)])
# [1, 2, 3, 5, 8, 13, 21, 34, 55] -> Fibonacci 位移一位
實驗 3:二階遞迴的通解結構。 \(a_{n+2} = 5 a_{n+1} - 6 a_n\) ,特徵方程 \(r^2 - 5r + 6 = 0\) 的根是 2 與 3:
from sympy import Eq, solve
r = symbols('r')
print(solve(Eq(r**2 - 5*r + 6, 0), r)) # [2, 3]
sol2 = rsolve(f(a + 2) - 5*f(a + 1) + 6*f(a), f(a), {f(0): 1, f(1): 4})
print(simplify(sol2)) # 2*3**a - 3*2**a?算一下:2*3**a - 2**a
# 結果會是 c1*2**a + c2*3**a 的形式,係數由初始條件決定
觀察重點: 1. 遞迴式給「怎麼算」,閉式給「是什麼」——rsolve 幫我們跨越這一步。 2. 線性齊次遞迴的閉式由特徵根決定,矩陣特徵值與遞迴特徵根是同一件事的兩面。 3. 河內塔 \(T_n = 2^n - 1\) 說明「指數爆炸」: \(n=64\) 時步數超過 \(10^{19}\) ,這是世界末日傳說的數學根據。
小結與練習#
本節我們學會了: - 遞迴關係式的概念與 rsolve() 的用法(遞迴式、函數、初始條件三個參數)。 - Fibonacci 的 Binet 公式 \(F_n = \frac{\varphi^n - \psi^n}{\sqrt{5}}\) , \(\varphi\) 為黃金比例。 - 用矩陣 \(A = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}\) 的特徵值理解遞迴的閉式結構(連結 6.3)。 - 解出河內塔遞迴 \(T_n = 2T_{n-1} + 1\) 的閉式 \(T_n = 2^n - 1\) 。
練習: 1. 用 rsolve 解 \(a_{n+1} = 3a_n + 2\) , \(a_0 = 1\) ,並驗證前 5 項。 2. 解遞迴 \(a_{n+2} - 4a_{n+1} + 4a_n = 0\) , \(a_0 = 1\) , \(a_1 = 3\) (注意重根時解的形式是 \((c_1 + c_2 n) 2^n\) )。 3. 用矩陣 \(A^{10}\) 計算 \(F_{11}\) ,與 rsolve 的結果比對。 4. 某人存款年利率 5%,每年年底再存 10000 元,寫出餘額的遞迴式並用 rsolve 解出 \(n\) 年後的餘額。