如何優雅的解決斐波那契數列
斐波那契數列是一個出鏡率極高,這個問題非常有意思,它看似簡單,其實裏面有很多的門道,從最初級的程序員到算法高手,都能寫出來答案,那麼我們嘗試去研究下它的“最叼”解。
常見的解法以及分析
- 時間複雜度:O(n^2)
let fibnacci = n =>
n <= 0 ? 0 : n == 1 ? 1 : fibnacci(n-2) + fibnacci(n-1);
將遞歸
改爲動態規劃
- 時間複雜度:O(n)
let fibnacci = n => {
if(n == 0) return 0p;
let a1 = 0, a2 = 1;
for(let i = 1; i < n; i++){
[a1, a2] = [a2, a1+a2];
}
return a2;
}
更好的解法?
數學思想——通項公式
let fibnacci = n =>
(Math.pow((1 + Math.sqrt(5))/2, n) - (Math.pow(1 - Math.sqrt(5))/2, n)) / Math.sqrt(5)
注意!通項公式使用了內置函數 Math.pow,它的時間複雜度並不低,思路上是正確的,我們就從實現上改一改
自己實現一個Math.pow() ——讓冪運算的時間複雜度是無限接近 O(log(n))
let pow = (x ,y) => {
var r = 1;
var v = x;
while(n) {
if(n % 2 == 1){
r *= v;
n-=1
}
v = v * v;
n = n / 2
}
return r;
}
此外這個實現方式有一些瑕疵,需要取整
利用矩陣的方式求斐波那契數列
矩陣乘法(此時不得不感嘆大學裏學的線性代數都還給老實了。。。)
我們將這種數學思想實現的pow方法整理一下
let matrix22_pow = (x, n) => {
var r = [[1, 0][0, 1]]
var v = x
while(n){
if(x % 2 == 1) {
r = matrix_mul(r, v)
n -= 1;
}
v = matrix_mul(v, v)
n = n / 2
}
return r
}
在將次pow方法套用到斐波那契數列的解法中
最終解
let fibnacci = n =>
n <= 0 ? 0 :
matrix_mul( [[0, 1],[0, 0]] , matrix22_pow([[0, 1],[1, 1]], n-1) )[0][1]