Fibonacci(斐波納契)數列求解 zz

2021-05-06 17:24:08 字數 2879 閱讀 9207

unsigned long fib(int n)

else

}

遞迴演算法與定義公式十分吻合,容易理解,但計算過程存在大量重複的運算,時間複雜度達到了o(2^n),使用的記憶體空間也隨著函式呼叫棧的增長而增長。這顯然不適於實用的程式。

2、表驅動的遞迴法。這裡不提純粹的表驅動法,因為對於項數未知的fibonacci數列開啟大片的空間來換取時間未免不值得且不負責。我們只是為了消除遞迴法中大量重複的運算,可以將已經計算過的中間值存入乙個表,已備後續使用:

#define max_log 20

static unsigned long logs[max_log] = ;

unsigned long fib(int n)

else if (n < max_log && logs[n] != 0) else

}

當n小於儲存的表長時,由於每個中間值只計算一次,時間複雜度降為o(n)。但隨著n的增大,要想維持o(n)的時間複雜度,就必須擴大儲存的表長,這就造成了儲存空間的浪費。

3、迭代法。求fibonacci數列第n項時雖然要用到前面兩項的值,但它們僅作為臨時計算的中間值,不作為結果輸出,因此無保留的必要,完全可以轉化成迭代法求解:

unsigned long fib(int n)

else

return c;

}}

迭代法的時間複雜度為o(n),使用的記憶體空間也不會動態**。個人認為fibonacci數列更適宜作為迭代法而非遞迴法的典例出現在教材上。

下面給出兩種數學性較強的演算法。考慮到表達的簡潔性,用matlab實現:

4、轉移矩陣法。此方法通常見於線性代數中的markov過程示例。fibonacci數列第n項與第n-1項可以通過轉移矩陣的n-1次迭代求出:

**如下:

function y = fib(n)

first = [1; 0];

trans = [1 1; 1 0];

last = trans ^ (n - 1) * first;

y = last(1, 1);

end

此演算法的時間複雜度相當於計算矩陣乘方的時間複雜度。在計算2階矩陣n次方時,如果直接按矩陣乘法定義式展開,不加特別優化,其時間複雜度為o(n)。

5、通項公式法。fibonacci數列的通項公式如下(證明略):

利用通項公式可以得到fibonacci數列的任何項:

function y = fib(n)

sr5 = sqrt(5);

y = uint32((((1 + sr5) / 2) ^ n - ((1 - sr5) / 2) ^ n) / sr5);

end

該方法的時間複雜度貌似為o(1),但我們還應該考慮乘方運算的時間消耗。不加特別優化時,用乘法實現n次乘方的時間複雜度為o(n)。考慮到浮點數計算效率和精度問題,此方法在計算機實現時不如轉移矩陣法。

下面再給出兩種對計算機實現有特別意義,但同時有一定侷限性的實現方法(只是實現方法,不能稱為新的演算法)。其中使用了一些c++程式設計技巧,對使用其它語言實現也有一定的參考價值:

6、模板元程式設計法。通常我們在c++中使用模板,僅限於類模板與函式模板。事實上c++支援模板元程式設計,理論上可以在編譯時執行任何計算(甚至包含選擇、迴圈、遞迴等結構)。**如下:

#define fib(n) fibt::val

templatestruct fibt;};

template<> struct fibt<0>;};

template<> struct fibt<1>

;};

我們用乙個結構體作為模板的載體,用乙個列舉值儲存運算結果。其中第乙個模板為基本遞迴過程(使用遞迴演算法是為了說明的簡便,完全可以用其它演算法替代,以加速編譯過程),後兩個模板為n=0、1時的模板特化。通過#define語句將模板呼叫簡寫成類似函式呼叫的方式。程式在編譯時運算所需的fibonacci數列項,將結果作為常量嵌入編譯好的程式。執行時直接使用結果,時間複雜度真正地變成了o(1)。但這一方法最大的侷限就是只能對常量嵌入,程式中出現諸如計算fib(i++)的情況則無能為力。儘管如此,這比在**中手工計算並寫入所需的值要直觀準確,比通過純粹的表驅動法「空間換時間」要方便快捷。

7、函式物件法。此方法主要用於c++ stl程式設計的通用演算法方面,其執行行為也有別於以上其它方法:

class fib

unsigned long operator()()

else

}private:

int a, b, n;

};

這個函式類物件的行為可以理解為乙個「fibonacci數列發生器」,其測試性呼叫如下,程式將依次列印

void test(int i)

while (i--);

}

函式物件具有與函式指標類似的行為,同時又能儲存自身的一些屬性,因此常用於stl通用演算法程式設計。但針對單個的fibonacci數列項求值,靈活性不如一般的方法。

希望讀者能夠從上面的演算法分析中舉一反三,有所領悟。

參考資料:

1、bruce eckel,thinking in c++ volume 2: practical programming,機械工業出版社,2006

2、william j. collins,data structures and the standard template library,機械工業出版社,2003

斐波納契數列 fibonacci

查詢斐波納契數列中第 n 個數。所謂的斐波納契數列是指 前2個數是 0 和 1 第 i 個數是第 i 1 個數和第i 2 個數的和。斐波納契數列的前10個數字是 0,1,1,2,3,5,8,13,21,34 實現兩種方式 1。遞迴 實現相對簡單,但遞迴執行效率低,產生的時間複雜度為指數階o 2 n ...

斐波納契數列

f 1 0 f 2 1 f n f n 1 f n 2 斐波納契數列決定審美和諧性 800年前,義大利的數學家李奧納多 斐波那契出版了驚世之作 算盤書 在 算盤書 裡,斐波納契提出了著名的 兔子生兔子的問題 有乙個人把一對兔 子放在四面圍著的地方。假定每個月一對兔子生下另外一對。而這新的一對在二個月...

斐波納契數列

斐波納契數列又稱 分割數列 因數學家列昂納多 斐波那契 leonardoda fibonacci 以兔子繁殖為例子而引入,故又稱為 兔子數列 指的是這樣乙個數列 1 1 2 3 5 8 13 21 34 此本章通過多種方式實現斐波納契數列 第一種 for 迴圈實現 a,b 0,1 for i in ...