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); }
  1. 動態規劃
    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;
}

--------------------上面的東西足夠應付面試-----------------------
-------------------下面只是本人藉著演算法導論開腦洞的結果-----------------

  1. 矩陣快乘
    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))…比前面的都要屌。

  1. 斐波那契通項公式
    這個東西就不多介紹了。。因為百度百科上有,好多種方法。。。而且演算法導論本身也給了一種。 第三版的4-4裡面也給了一個。用生成函式的辦法做的。
    總之結果就是這個東西,大概是 ((( 1 + sqrt(5) ) / 2) ^ n) / sqrt(5)。

  2. 基於位運算的批判與重新思考
    這個部分是毀三觀的,無破則無立。
    首先我們通過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)其實是名義上的。這大概就是演算法導論題目裡面說的斐波那契數的終極奧義了。

  3. 大數乘法的歸併運算與究極思考。
    額,前面約定”約定兩個n/2數相乘需要 O(N*N/4)的時間”,這個東西能不能改進呢?,結果當然是能。
    比如用演算法導論30-1的方法,我們可以 在 O(N的log3次方)時間內算出來。 所以整體的時間複雜度優化成 O(N的log3次方)
    由於這個東西用到了歸併運算,可以考慮用演算法導論第27章的知識進行優化。實現多核同步運算。。。
    這個都究極時間複雜度大概是演算法導論 第31章第一小部分裡面講的 那個 O(N logN loglogN)

  4. 扯蛋的擴充套件,
    如果我們可以一次邁1,2,3階該怎麼辦。
    很簡單啊,
    f(n) = f(n-1) + f(n-2) + f(n-3)。。然後照著做就可以了。矩陣哪裡也改一下就可以了。

相關推薦

樓梯問題說

正式講一下斐波那契數: 首先我們知道有一個著名的演算法面試題: 一共有n個臺階,你一次可以走一個臺階,或者兩個臺階。那麼,走到臺階頂時,一共有多少種走法。 比如三個臺階,你可以 1,2。。。或者1,1,1 或者2,1。。一共三種走法。 網路上會有幾種做法

_樓梯問題說

正式講一下斐波那契數: 首先我們知道有一個著名的演算法面試題: 一共有n個臺階,你一次可以走一個臺階,或者兩個臺階。那麼,走到臺階頂時,一共有多少種走法。 比如三個臺階,你可以 1,2。。。或者1,1,1 或者2,1。。一共三種走法。 網路上會有幾種做法, 1.直接思路

java--Fibonacc由數字1、1、2、3...組成的,第三個數字起每一個數字為前兩個數字的和。建立一個方法,接受一個整數引數,顯示第一個元素開始總共由該引數指定的個數所構成的所有斐波那契數

題目完整描述:一個斐波那契數列是由數字1、1、2、3、5、8、13、21、34等等組成的,其中每一個數字(從第三個數字起)都是前兩個數字的和。建立一個方法,接受一個整數引數,並顯示從第一個元素開始總共由該引數指定的個數所構成的所有斐波那契數字。例如,如果執行 java Fibonacci 5(Fib

的python語言實現---遞歸和叠代

put bsp print span return spa number n-2 遞歸實現 叠代實現如下: def fab(n): n1 = 1 n2 = 1 if n<1: print

SICP 1.2.2 樹形遞歸 ()

mce oid nbsp dig efi del 叠代 reat public (define (fib n) (cond ((= n 0) 0) ((= n 1) 1) (else (+ (fib (- n 1))

HDU 1568 Fibonacci【求的前4位/遞推式】

urn content new targe 接下來 bsp hide 斐波那契 href Fibonacci Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other

問題 : 來簡單地個數(大數模擬計算+區間

sample 一個 輸入數據 輸出 一個數 兩個 turn led ycm 題目描述 這是一個斐波那契數列: f1 = 1 f2 = 2 fn = fn-1 + fn-2 (n>=3) 蔡老板想知道,給你兩個數 a、b,你能否求出在區間[a,b]裏有多少個斐波那

php 兩種方式實現求

機器 XP 方式 一個 urn 性能 耗時 exec [1] 使用遞歸方式。 //使用遞歸方式求斐波那契數 public function fb($n){ // if( $n <=2){ return 1;

Python腳本得出

cep NPU 得出 except str end eas while循環 需要 首先定義函數,然後在while循環中調用函數,得到自己需要的結果def getfib(num): fib=[1,1] for i in range(num+1):

HDU 1021(與因子3 **)

判斷 pla 斐波那契 因子 show hid 分享 close one 題意是說在給定的一種滿足每一項等於前兩項之和的數列中,判斷第 n 項的數字是否為 3 的倍數。 斐波那契數在到第四十多位的時候就會超出 int 存儲範圍,但是題目問的是是否為 3 的倍數,也就是模

實現求第n個

斐波那契數列:指的是這樣一個數列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368..這個數列從第3項開始,每一項都等於前兩項之和 求第n個斐

C語言----的n種實現方法

斐波那契數列(Fibonacci):第1,2兩個數為1,1。從第三個數開始,該數是其前面兩個數之和。 1.使用簡單程式碼實現 (1)每次迴圈只輸出後一位數 思想:前兩個數為1,1。先定義前兩個變數a,b。第三個數為前兩兩個數之和,定義第三個變數c,c=a+b;現在有三個數,為了避免冗餘,把

求第n個(分別用遞迴和非遞迴兩種方法求解)

斐波那契數列指的是這樣一個數列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55……這個數列從第3項開始,每一項都等於前兩項之和。 這裡分別用遞迴和非遞迴的方法實現: 遞迴 #define _CRT_SECURE_NO_WARNINGS 1 #include&l

用遞迴求

斐波那契數是第一個數和第二個數都為1,從第三個數開始,後面的是是前面相鄰兩個數的和。定義的函式如下所示: int fib(int m) {     if (m == 1 || m == 2)         

Java實現高效演算法

前段時間去面試,被問到了斐波那契數演算法,在此回顧總結一下。 1、什麼是斐波那契數演算法 斐波那契數,亦稱之為斐波那契數列(義大利語: Successione di Fibonacci),又稱黃金分割數列、費波那西數列、費波拿契數、費氏數列,指的是這樣一個數列:1、1、2、3、5、8、1

遞迴和非遞迴分別實現求第n個

//非遞迴 int main() { int a = 0; int b = 1; int c = 0; int i = 0; int n = 0; printf("請輸入數字n(n>2)求第n個斐波那契數:"); scanf("%d",&n); for(i =

非遞迴求

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int fib(int n) { int a = 1; int b = 1; int c = 0; if (n <= 2) re

求第n個

斐波那契數:簡單的講就是除了第1項和第2項是1以外,其它的每一項都等於前兩項的和,比如:1,1,2,3,5,8,13..... 在這用遞迴和非遞迴兩種方法實現: #define _CRT_SECUR

1.遞迴和非遞迴分別實現求第n個。 遞迴與非遞迴的典型題型

1.遞迴和非遞迴分別實現求第n個斐波那契數。 2.編寫一個函式實現n^k,使用遞迴實現 寫一個遞迴函式DigitSum(n),輸入一個非負整數,返回組成它的數字之和,例如,呼叫DigitSum(1729),則應該返回1+7+2+9,它的和是19 編寫一個

計算第n個

斐波那契數列 n>1,F(n)=F(n-1)+F(n-2); F(0)=0; F(1)=1; 求第n個斐波那契數 遞迴法 利用已知的斐波那契數的遞推公式即可 public class Fibonacci { public static i