經典兔子繁殖問題:一對兔子,出生後第 3 個月起每月產一對,新生兔子長到第 3 個月也開始產仔,假設不死。求第 n 月兔子總對數。這正是斐波那契數列:F(1)=1, F(2)=1, F(n)=F(n-1)+F(n-2)。
#include <stdio.h>
int f(int n); // 遞歸函數
int main() {
int month, i, sum = 0;
printf("請輸入月數: ");
scanf("%d", &month);
for (i = 1; i <= month; i++) {
sum += f(i); // ❌ 累加每月「新出生對數」?題目問「總數」
}
printf("第 %d 月,兔子總數 %d 對\n", month, sum);
return 0;
}
int f(int n) {
if (n == 1 || n == 2) return 1;
return f(n-2) + f(n-1); // 標準遞歸
}
f(n) 直接給出第 n 月總對數(斐波那契定義)。sum += f(i) 將每月總數累加,結果完全錯誤。正確應直接輸出 f(month)。
f(5) = f(4) + f(3)
= f(3)+f(2) + f(2)+f(1)
= f(2)+f(1)+1 + 1+1 ...
大量重複計算:f(3) 算 2 次,f(2) 算 3 次...
調用次數 ≈ 1.618ⁿ (黃金比例指數增長)
n=40 時需約 1.6 億次調用,明顯卡頓;n=50 約 200 億次,不可接受。
| 方案 | 時間 | 空間 | 代碼 |
|---|---|---|---|
| 遞歸 + 備忘錄 (自頂向下 DP) | O(n) | O(n) | 數組緩存 f(n) |
| 迭代 DP (自底向上,推薦) | O(n) | O(1) | 兩變量滾動 |
| 矩陣快速冪 | O(log n) | O(1) | 線性遞歸通用加速 |
| 通項公式 (Binet) | O(1) | O(1) | 浮點精度有限 |
long long fib_iterative(int n) {
if (n <= 2) return 1;
long long a = 1, b = 1, c;
for (int i = 3; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}
#define MAX_N 100
long long memo[MAX_N] = {0};
long long fib_memo(int n) {
if (n <= 2) return 1;
if (memo[n]) return memo[n];
return memo[n] = fib_memo(n-1) + fib_memo(n-2);
}
typedef struct { long long a, b, c, d; } Matrix;
Matrix mul(Matrix x, Matrix y) {
return (Matrix){
x.a*y.a + x.b*y.c, x.a*y.b + x.b*y.d,
x.c*y.a + x.d*y.c, x.c*y.b + x.d*y.d
};
}
Matrix mpow(Matrix base, int exp) {
Matrix res = {1,0,0,1}; // 單位矩陣
while (exp) {
if (exp & 1) res = mul(res, base);
base = mul(base, base);
exp >>= 1;
}
return res;
}
long long fib_matrix(int n) {
if (n <= 2) return 1;
Matrix M = {1,1,1,0}; // 斐波那契矩陣
Matrix R = mpow(M, n-2);
return R.a + R.b; // F(n) = R.a*F(2) + R.b*F(1)
}
| 類型 | 最大 n | F(n) 值 |
|---|---|---|
| int (32位) | 46 | 1,836,311,903 |
| unsigned int | 47 | 2,971,215,073 |
| long long (64位) | 92 | 7,540,113,804,746,346,429 |
| unsigned long long | 93 | 12,200,160,415,121,876,738 |
n>93 需大整數庫或高精度模擬。
int main() {
int month;
printf("請輸入月數 (1-92): ");
if (scanf("%d", &month) != 1 || month < 1) return 1;
printf("第 %d 月兔子總對數: %lld\n", month, fib_iterative(month));
return 0;
}
兔子繁殖問題是斐波那契數列的經典建模來源。原代碼暴露了遞歸理解偏差(累加總數)與指數級遞歸效率陷阱。關鍵收穫:
F(n) = F(n-1) + F(n-2),直接給出第 n 項值,無需累加;long long 支持到 n=92,超過需大整數。