「有一對兔子,從出生後第三個月起每個月都生一對兔子……」這個著名的斐波那契數列起源問題,不僅是遞歸教學的經典案例,更是動態規劃思想的完美入門場景。本文將從數學建模、遞歸實現、迭代優化、矩陣快速冪四個層次徹底攻克此題。
設F(n)為第n個月兔子總對數:
F(1) = 1(初始一對)F(2) = 1(第二月仍只有一對,未成熟)F(n) = F(n-1) + F(n-2)(n≥3:上月所有兔子存活 + 新出生的 = 兩月前已成熟的兔子數)這正是斐波那契數列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...
public long fibRecursive(int n) {
if (n <= 2) return 1;
return fibRecursive(n-1) + fibRecursive(n-2);
}
// 時間複雜度:O(2^n),指數級爆炸,n=50時需數小時
Map memo = new HashMap<>();
public long fibMemo(int n) {
if (n <= 2) return 1;
if (memo.containsKey(n)) return memo.get(n);
long res = fibMemo(n-1) + fibMemo(n-2);
memo.put(n, res);
return res;
}
// 時間/空間:O(n),但遞歸深度受棧限制
public long fibIterative(int n) {
if (n <= 2) return 1;
long prev2 = 1, prev1 = 1, curr = 0;
for (int i = 3; i <= n; i++) {
curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return curr;
}
// 時間O(n),空間O(1),無棧溢出風險
public long fibMatrix(int n) {
if (n <= 2) return 1;
long[][] base = {{1,1},{1,0}};
long[][] res = matrixPow(base, n-2);
return res[0][0] + res[0][1]; // F(n) = M^(n-2)[0][0]*F(2) + M^(n-2)[0][1]*F(1)
}
// 時間O(log n),適合n極大(如10^9)場景
public class RabbitProblem {
public static void main(String[] args) {
int months = 12;
System.out.println("月份\t兔子對數");
for (int i = 1; i <= months; i++) {
System.out.println(i + "\t" + fibIterative(i));
}
}
// 推薦:迭代版本,兼顧性能與可讀性
private static long fibIterative(int n) {
if (n <= 2) return 1;
long a = 1, b = 1, c = 0;
for (int i = 3; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
}
long溢出,需用BigIntegerF(n) = (φ^n - ψ^n)/√5,但浮點精度限制實用性兔子問題看似簡單,實則蘊含「狀態定義→轉移方程→邊界條件→優化空間」的動態規劃核心思想。掌握其多種解法演進過程,是算法修煉的重要里程碑。