1. 程式人生 > >斐波那契數_從樓梯問題說開去

斐波那契數_從樓梯問題說開去

正式講一下斐波那契數:

首先我們知道有一個著名的演算法面試題:

一共有n個臺階,你一次可以走一個臺階,或者兩個臺階。那麼,走到臺階頂時,一共有多少種走法。

比如三個臺階,你可以 1,2。。。或者1,1,1 或者2,1。。一共三種走法。

網路上會有幾種做法,

1.直接思路:

這好像是這道題目的標準“解法”

如果我們現在在n階,那麼我們可以邁一步或者邁兩步,結果就是

f(n) = f(n-1) + f(n-2)。。。即n的臺階的種數應該是,n-1階的種數與n-2階種數之和(由於第一步就不一樣,所有,後面的二者互相獨立)。

所以

int f(int n) {

if(n ==0 || n == 1) {

return 1;

}

return f(n-1)+f(n-2);

}

2. 動態規劃

        1的東西雖然給出了這個題的一個突破口,但還是有些問題比如:第1中的時間複雜度時是指數次的,過於龐大。。

所以可以考慮用動態規劃。。第三版第15章的動態規劃內容

如果有我們可以預先紀錄下f(n-1),或者f(n-2),避免重複計算。

所以

a[n] = {0};

a[0] = 1; a[1] = 1;

int f(int n) {

if(a[n] != 0) {

return a[n];

}

a[n] =  f(n-1)+f(n-2)

return a[n];

}

這個時間複雜度就比較好了,空間和時間都是O(n) 。(當然後面我們會看到,這麼說不是很恰當)

3. 動態規劃二        2的思路比較好了,但是一來有比較大的空間複雜度,二來,遞迴呼叫,讓人不爽。 所以我們可以用演算法導論(動態規劃)那一章的另一個思路直接正著計算。 結果出乎意料的簡單:

int f(int n) {

int a = 1; b = 1;

int c = 0; 

for(int i = 1; i < n; i++) {

c = a;

a = a+b;

b = c;

}

return a;

}

--------------------上面的東西足夠應付面試----------------------- -------------------下面只是本人藉著演算法導論開腦洞的結果----------------- 4.  矩陣快乘
        3的解法已經很不錯了。基本上就可以了,下面即將開啟二逼的求知之旅。。這是演算法導論,第三十一章的後面大題 考慮2*2矩陣    0  1    1  1 這是一個 如果我們將這個矩陣乘以 A1 , A2  0 1        A1         A2  1 1   *    A2    =   A1+A2 由於斐波那契數列, A3 = A1+A2 ...結果我們發現這個東西其實是實現了 A1, A2 到 A2,A3的轉換。。。 如果我們用 A2,A3, 去乘,結果自然是 A3 , A2+A3 也就是 A3,A4 所以我們可以這麼寫,我們令M表示矩陣  ,  0  1   1  1 那麼    (M .... (M* (M * (A1,A2)T))...) = (An-1, An),其中一共有n-2層的M. 考慮到相容矩陣相乘具有如下性質 (M*N)*Q = M*(N*Q). 所以變換括號,(M*M*.....M)*(M*M.....*M)*(A1, A2)T 其中我們讓那些括號裡面的M個數相同(如果是奇數的話單獨拎一個M到最外層),結果,我們其實只需要算出前面的,後面的也不用去算了。。 這個就是分治的矩陣快乘。 名義上的時間複雜度是O(log(N))...比前面的都要屌。 5. 斐波那契通項公式        這個東西就不多介紹了。。因為百度百科上有,好多種方法。。。而且演算法導論本身也給了一種。 第三版的4-4裡面也給了一個。用生成函式的辦法做的。 總之結果就是這個東西,大概是 ((( 1 + sqrt(5) ) / 2) ^ n)  /  sqrt(5)。 6. 基於位運算的批判與重新思考 這個部分是毀三觀的,無破則無立。 首先我們通過5可以知道,這個斐波那契數,隨著n幾乎是一個指數式的增長。。。位數大概是:log(( 1 + sqrt(5) ) / 2) ^ n)  ) 就是 n*log(( 1 + sqrt(5) ) / 2) ) =>  O(N) 我們方便計算就說它有n位的二進位制數吧。  現在所有的計算機都是有限位數的,32位,64位機器。。或者128位。。。所以 n/128 其實也是 O(N)。。也就是說,我們這個n稍微大一點(比如說1000),那麼前面的那些1,2,3部分程式碼的int 型別就不夠了。。即使用long long 也是不夠的。。。 具體可以用大整數這個東西處理。 下面是31-3的第d小題的分析 如果我們用大整數處理,那麼時間空間複雜度的結果會有問題,比如“2裡面說:空間和時間都是O(n)”。這個就不恰當。空間上,到最後的數有n位,那麼空間複雜度自然也就是(1+2+3+...+n) = O(N*N)..時間上其實也是這樣的,N位數N-1位數相加,時間複雜度自然是O(N)。。。所以時間複雜度自然是 (1+2+3+...+n) = O(N*N) 3的情況與2類似,只是空間複雜度變小了一點而已。。。。 對於4的矩陣情況,我們知道最後是兩個幾乎一樣的矩陣相乘最後得到一個 n位的結果,那麼很明顯,我們大概需要 n/2的數乘以 n/2位的數,由於31-3的d小題,約定兩個n/2數相乘需要 O(N*N/4)的時間。結果自然就是時間複雜度O(N*N)...那個 log(N)其實是名義上的。這大概就是演算法導論題目裡面說的斐波那契數的終極奧義了。 7. 大數乘法的歸併運算與究極思考。 額,前面約定"約定兩個n/2數相乘需要 O(N*N/4)的時間",這個東西能不能改進呢?,結果當然是能。 比如用演算法導論30-1的方法,我們可以 在 O(N的log3次方)時間內算出來。 所以整體的時間複雜度優化成 O(N的log3次方) 由於這個東西用到了歸併運算,可以考慮用演算法導論第27章的知識進行優化。實現多核同步運算。。。 這個都究極時間複雜度大概是演算法導論 第31章第一小部分裡面講的 那個 O(N logN loglogN) 8. 扯蛋的擴充套件, 如果我們可以一次邁1,2,3階該怎麼辦。 很簡單啊, f(n) = f(n-1) + f(n-2) + f(n-3)。。然後照著做就可以了。矩陣哪裡也改一下就可以了。