斐波那契數列演算法優化問題
阿新 • • 發佈:2019-01-25
斐波那契是數學中最值得討論的一個問題,從12世紀斐波那契提出這個數列後,就有很多數學家研究過這個數列,對斐波那契數列的新發現也越來越多,這些細節我沒能力去研究,這篇文章中要講的是程式設計中對生成斐波那契數演算法的優化。首先要說的就是斐波那契數列的定義,這一切都起源於一個生殖能力超強的兔子:
- 第一個月初有一對剛誕生的兔子
- 第二個月後(第三個月初)他們可以生育
- 每月沒對兔子可生育的兔子會誕生下一對新兔子
- 兔子永不死去
幾乎每個學計算機的在學程式語言的時候都會遇到這樣的習題:計算第N個月兔子的總數
點選這裡檢視完整原始碼,建議對著完整的程式碼除錯。
最簡單的遞迴演算法
老師肯定會教的一種方法:
uint64_t fibonacci(unsigned int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
該方法來自於斐波那契數列的一個遞推式:
fib(n) = fib(n-1) + fib(n-2)
然後使用遞迴演算法並指定遞迴出口,即可得出結果。
使用迴圈迭代消除遞迴
遞迴因為要不斷地呼叫函式自身,呼叫函式就伴隨著引數以及函式區域性變數入棧,當遞迴層數較大容易產生棧溢位,所以通常需要我們使用迴圈優化遞迴演算法。幸運地是,大多數遞迴都能修改成迴圈(使用自定義棧儲存變數的方式仍然算遞迴)。而且上面的演算法在效率上存在很大的優化空間:
你會發現fib(5) = fib(4) + fib(3),而求fib(4)的時候我們已經求過fib(3),這意味著我們做了很多重複的工作,很明顯我們需要把前面做過的工作暫存。
遞迴演算法時間呈指數形式增長:O(2^N);而使用迴圈迭代時間上呈線性增長:O(N)。在我筆記本上測試時,當n超過40遞迴演算法的時間就開始爆炸了。
uint64_t fibonacci(unsigned int n) {
if (n == 0) return 0;
if (n == 1 || n == 2) return 1;
uint64_t f1 = 1, f2 = 1, fn;
for (unsigned int i = 3; i <= n; i++) {
fn = f1 + f2;
f1 = f2;
f2 = fn;
}
return fn;
}
矩陣演算法求解
斐波那契數列的遞推公式是:fib(n) = fib(n-1) + fib(n-2);我們可以用矩陣來表示這種關係:
進一步推到可以得到:
從0開始算得到Fn則需要更進一步:
我們要實現一下矩陣運算: