[極客時間-每日一課]如何優雅地計算斐波那契數列?

課程:https://time.geekbang.org/dailylesson/detail/100028406javascript

問題:計算斐波那契數列的第n項的值,數列表達式:F[n]=F[n-1]+F[n-2] (n>=2,F[0]=0,F[1]=1)html

winter(講師)認爲這是一道很好的面試題,java

  1. 答案簡單。面試

  2. 每一個面試者都能寫出點東西。數組

  3. 區分度高,不一樣水平的人寫出來的代碼水平不一樣。less

  4. 馬甲衆多(不多有面試官直接上來講給我擼一個斐波那契數列,都會問一個具體的問題,最後歸根到底就是斐波那契數列問題)。函數

 

好,接下來看一下winter認爲不一樣水平的代碼長啥樣。性能

 

Lev1. 遞歸優化

int Fibnacci(int n){
    if(n < 2){
        return n;
    }
    return Fibnacci(n - 1) + Fibnacci(n - 2);
}

能夠簡單算一下它的時間複雜度是指數級的。它會把子問題重複計算多遍。若是我要計算數列第5項的值,會計算以下中間結果。spa

 

 

 能夠觀察到純在大量重複的節點計算。優化一下。

 

Lev2. 帶備忘錄的遞歸

int Fibnacci(int n){
    if(map.ContainsKey(n)){
        return map[n];
    }
    if(n < 2){
        return n;
    }
    int res = Fibnacci(n - 1) + Fibnacci(n - 2);
    map.Add(n, res);
    return res;
}

時間複雜度O(n), 空間複雜度O(n)。到這有人說遞歸自帶性能消耗,再優化一下。

 

Lev3. DP (動態規劃)

當發現存在大量重複子問題的時候,一般咱們會想到DP.

首先咱們肯定狀態轉移方程 DP[n] = DP[n-1] + DP[n-2].

(DP是一種自下向上的解決問題的思路,先解出f(2), 那f(3)就得解,接着f(4)也就得解,直到f(n),而遞歸是自上而下)

int Fibnacci(int n){
    if(n < 2){
        return n;
    }
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    for(int i = 2; i <= n; i++){
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

時間複雜度O(n), 空間複雜度O(n)。

能夠繼續優化把DP數組去掉,空間複雜度優化成O(1),這裏就不演示了。

到這,其實我認爲這已是極限了,最起碼是個人極限。

 

Lev4. 通項公式

沒錯,數學家給出了數列的通項公式,咱們老老實實套公式便可。

 

 

 感興趣的能夠本身推導一遍。

let fibnacci = (n) => ((Math.pow(1 + Math.sqrt(5))/2, n) - Math.pow((1 - Math.sqrt(5))/2, n))/Math.sqrt(5);  

問題來了,如今的時間複雜度是O(1)嗎?嚴格意義上說不是,這裏調用了系統的冪函數,winter沒有指出該函數在V8的具體實現,可是結論必定不是O(n), 更不是O(1)

後面他提供了本身實現的O(log(N))冪運算版本:

let pow = (x, n) => {
    var r = 1;
    var v = x;
    while(n) {
        if(n % 2 == 1){
            r *= v;
            n-= 1;
        }
        v = v * v;
        n = n /2;
    }
    return r;
}

Lev N: ????

考慮到浮點偏差,winter再次提出藉助線性代數的矩陣運算來表示斐波那契數列的通項。

到這裏,我已經完全放飛自我,以爲他說的都對。

 

總結

這節課的後半段,體驗不是很好,畢竟數學知識全還了,思路已經跟不上了。

常見的斐波那契數列題型:

1. 爬臺階:有個小孩正在上樓梯,樓梯有n階臺階,小孩一次能夠上1階、2階。實現一種方法,計算小孩有多少種上樓梯的方式(還有什麼青蛙跳臺問題,換一下主語)

2. 爬臺階+: 有個小孩正在上樓梯,樓梯有n階臺階,小孩一次能夠上1階、2階、3階。實現一種方法,計算小孩有多少種上樓梯的方式

3. 兔子繁殖問題: 一對兔子每月能生出一對小兔子來。若是全部兔子都不死,那麼一年之後能夠繁殖多少對兔子?

 

PS: 和斐波那契數列類似的著名數列:卡塔蘭數列

具體問題:給定一個整數 n,求以 1 ... n 爲節點組成的二叉搜索樹有多少種?

假如n=3,結果就是5種。

卡特蘭的隨筆已補:http://www.javashuo.com/article/p-ykkmuaye-cc.html

相關文章
相關標籤/搜索