斐波那契數列的四種解法(頭遞歸、尾遞歸、迭代與矩陣快速冪)


斐波那契數列

斐波那契數列指的是這樣一個數列:

$0, 1, 2, 3, 5, 8, 13, 21...$

后面的每一個數是前面緊鄰的兩個數之和

$$F(n) = \begin{cases} 0, &n = 0 \\ 1, &n = 1 \\ F_{n-1} + F_{n-2} &n \ge 2\end{cases}$$

注意:斐波拉契數列第26個突破十萬,第31個突破一百萬,第36個突破一千萬,第40個突破一億,第45突破10億,第50突破100億。(數列從坐標1開始計數)

由於數列增長快,int類型只能覆蓋到近50位,所以盡可能采用long long類型來存儲數據。】

通項公式

斐波那契有一個通項公式:$$F(n) = \frac{1}{\sqrt{5}} \times \left[ (\frac{1 + \sqrt{5}}{2}) ^n - (\frac{1 + \sqrt{5}}{2})^n\right]$$

頭遞歸實現(C)

//時間復雜度O(N^2)
//空間復雜度O(N)
long long fib(size_t n) {
    return (n < 2) ? n : (fib(n - 1) + fib(n - 2));
}

遞歸樹

 

從圖中可以看出:計算F(10)要計算F(9)F(8),而要計算F(9),又要計算F(8),以此類推,要計算很多重復的值,這樣就浪費了時間,而計算重復值的數量隨着N值而急劇增大,事實上該算法的時間復雜度隨着n值呈指數增長。不信,大家可以取N = 100看看遞歸要慢到什么程度。

缺點

(1)只適用於n比較小的時候,否則效率低,因為會做很多次重復操作
(2)該例遞歸屬於多分支遞歸,容易造成棧溢出

尾遞歸實現(C)

//時間復雜度O(N)
long long fib(long long first, long long second, size_t n)
{
    if(n<2) {
        return n;
    }
    if(2 == n) {
        return first+second;
    }
    return fib(second, first+second, n-1);
}

優點

(1)計算結果參與到下一次的計算,從而減少很多重復計算量;

(2)原本朴素的遞歸產生的棧的層次像二叉樹一樣,以指數級增長,但是現在棧的層次卻像是數組,變成線性增長了。簡單來說,原本棧是先擴展開,然后邊收攏邊計算結果,現在卻變成在調用自身的同時通過參數來計算;

(3)部分編譯器對於尾遞歸有特別進行優化,可以提升性能;

總結

尾遞歸的本質是:將單次計算的結果緩存起來,傳遞給下次調用,相當於自動累積

尾遞歸可以轉換成迭代算法

迭代實現(C)

//時間復雜度O(N)
//空間復雜度O(1)
long long fib(size_t n) {
    long long n1 = 0, n2= 1, n3=n;
    int i = 0;
    for(i = 2;i<=n;i++) {
        n3 = n1+n2;
        n1 = n2;
        n2 = n3;
    }
    return n3;
}

矩陣乘法實現(矩陣快速冪)

關於矩陣快速冪的詳細介紹見矩陣快速冪詳解一文,斐波拉契數列作為快速冪的一個經典應用,這里直接上公式:

公式

代碼實現(C++)

#include<iostream>
#include<string>
using namespace std;
 
 //定義2×2矩陣;
struct Matrix2by2 {
     //構造函數
     Matrix2by2(
         long m_00,
         long m_01,
         long m_10,
         long m_11
     ) :m00(m_00), m01(m_01), m10(m_10), m11(m_11) {}
 
     //數據成員
     long m00;
     long m01;
     long m10;
     long m11;
 };
 
 //定義2×2矩陣的乘法運算
Matrix2by2 MatrixMultiply(const Matrix2by2& matrix1,const Matrix2by2& matrix2) {
     Matrix2by2 matrix12(1,1,1,0);
     matrix12.m00 = matrix1.m00 * matrix2.m00 + matrix1.m01 * matrix2.m10;
     matrix12.m01 = matrix1.m00 * matrix2.m01 + matrix1.m01 * matrix2.m11;
     matrix12.m10 = matrix1.m10 * matrix2.m00 + matrix1.m11 * matrix2.m10;
     matrix12.m11 = matrix1.m10 * matrix2.m01 + matrix1.m11 * matrix2.m11;
     return matrix12;
 }
 
 
 //定義2×2矩陣的冪運算
Matrix2by2 MatrixPower(unsigned int n) {
     Matrix2by2 matrix(1,1,1,0);
     if(n == 1) {
         matrix = Matrix2by2(1,1,1,0);
     }
     else if(n % 2 == 0) {
         matrix = MatrixPower(n / 2);
         matrix = MatrixMultiply(matrix, matrix);
     }
     else if(n % 2 == 1) {
         matrix = MatrixPower((n-1) / 2);
         matrix = MatrixMultiply(matrix, matrix);
         matrix = MatrixMultiply(matrix, Matrix2by2(1,1,1,0));
     }
     return matrix;
 }
 //計算Fibnacci的第n項
long Fibonacci(unsigned int n) {
     if(n == 0)
         return 0;
     if(n == 1)
         return 1;
 
     Matrix2by2 fibMatrix = MatrixPower(n-1);
     return fibMatrix.m00;     
 }
 
 int main() {
     cout << "Enter A Number:" << endl;
     unsigned int number;
     cin >> number;
     cout << Fibonacci(number) << endl;
     return 0;
 }

(整理自網絡)

參考資料:

https://blog.csdn.net/qq_41035588/article/details/81814547


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM