求斐波那契數,你還在用遞迴嗎?
阿新 • • 發佈:2021-01-07
1、什麼是斐波那契數?
- 斐波那契數,又稱黃金分割數列、因數學家萊昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:0、1、1、2、3、5、8、13、21、34、……
- 斐波那契數,通常用F(n) 表示,形成的序列稱為斐波那契數列。該數列由0 和 1 開始,後面的每一項數字都是前面兩項數字的和。也就是:
F(0) = 0,
F(1)= 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
給定N,計算F(N)。
2、經典解法-遞迴
根據題意,我們可以用遞迴求解。
public int fib(int N) {
if (N < 2) {
return N;
}
return fib(N-1) + fib(N-2);
}
但是這個是最好的解法嗎?
- 假如我們要計算的N為10,我們要先計算fib(9),再計算fib(8);
- 計算fib(9), 要計算fib(8)和fib(7),如此遞迴下去
- 計算fib(8), 要計算fib(7)和fib(6),如此遞迴下去
發現什麼問題沒?
- 越往小了算,重複計算越多, 時間效率n的n次方,不可想象
- fib(30)耗時 4毫秒
- fib(50)耗時 62秒
- fib(100)耗時(1h沒結果,停了)
那有沒有其它的好辦法呢?
- 你發現沒有我們用遞迴是從大到小計算,從fib(N)到fib(N-1)...fib(0)
- 但是我們fib(N)不知道結果,我們只知道fib(0)和fib(1)
- 我們可不可以試著從小到大計算
3、動態規劃
我們嘗試從小往大推演,把計算結果儲存下來,避免重複計算,這種方法一般稱之為動態規劃。
public int fib(int N) {
if (N < 2) {
return N;
}
int[] cache = new int[N+1];
cache[0] = 0;
cache[1] = 1;
for (int i = 2; i <= N; i++) {
cache[i] = cache[i-1] + cache[i-2];
}
return cache[N];
}
- 上面的程式碼: cache[i] = cache[i-1] + cache[i-2]稱之為動態規劃公式
- 時間效率n, 遍歷一次即可
- 空間效率n,快取n個數據
- 空間效率還可以改進
4、動態規劃改進
- 我們用m代表fib(n-2), n代表fib(n-1), o代表fib(n)
- 每次計算結束再重置 m 和 n
public int fib(int N) {
if (N < 2) {
return N;
}
int m = 0, n = 1;
for (int i = 2; i <= N; i++) {
int o = n + m;
m = n;
n = o;
}
return n;
}
- 時間效率n, 遍歷一次即可
- 空間效率3
- 還有一種數學解法,有興趣的可以自己去了解