算法導論-求(Fibonacci)斐波那契數列算法對比

目錄                                                                                                

      一、斐波那契數列(Fibonacci)介紹

      二、樸素遞歸算法(Naive recursive algorithm)

      三、樸素遞歸平方算法(Naive recursive squaring)

      4 、自底向上算法(Bottom-up)

      五、 遞歸平方算法(Recursive squaring)

      六、完整代碼(c++)

      七、參考資料

內容                                                                                                 

      一、斐波那契數列(Fibonacci)介紹                

        Fibonacci數列應該也算是耳熟能詳,它的遞歸定義如上圖所示。html

        下面2-6分別說明求取Fibonacci數列的4種方法ios

      二、樸素遞歸算法(Naive recursive algorithm)

       在不少C語言教科書中講到遞歸函數的時候,都會用Fibonacci做爲例子。所以不少程序員對這道題的遞歸解法很是熟悉,看到題目就能寫出以下的遞歸求解的代碼c++

1  unsigned long long Fibonacci_naive_reu(int n)//樸素遞歸算法
2 {
3     int result[2] = {0,1};
4     if (n<2)       //退出條件
5        return result[n];
6 
7     return (Fibonacci_naive_reu(n-1)+ Fibonacci_naive_reu(n-2));//遞歸調用
8 }

 

其實這種想法很好,但該算法運行時間使人沒法忍受,當計算n=50時,用時1200多秒  程序員

分析:該算法的時間複雜度T(n)=Θ(∂n),其中,∂ = (1 + 5^½) / 2,即黃金分割比率。可知,∂>1,隨着n增長,時間指數快速增長,因此,平時不多采用!算法

      三、樸素遞歸平方算法(Naive recursive squaring)

這個算法主要根據斐波那契數列的一條數學性質而來。該性質代表,斐波那契數列F(n)即爲φ^n / 5^½向下取整。這樣,問題的求解因而變成了一個求x的n次方的問題,在以前的文章裏有講解,連接請進。 因此算法的效率爲Θ(lgn)。可是這個方法是不太靠譜的,主要是當n比較大時,因爲硬件的限制計算機中的浮點運算獲得的結果與真實值就產生偏差了。實用價值也不大,只能用作理論分析。編程

      4 、自底向上算法(Bottom-up)                           

 考慮到2中的簡單遞歸算法,爲了求解F(n),須要同時遞歸求解F(n - 1)和F(n - 2),顯然這樣就作了大量的重複工做。採用自底向上的算法便可避免這樣的冗餘。要計算F(n),則依次計算F0,F1,F2。。。Fn,這時計算Fn只須要利用前兩個結果便可,這樣算法效率提升到了Θ(n)。n=50時,小於1毫秒。實用價值高。數組

   算法代碼:ide

 1  /************************************************************************/
 2  /*                         自底向上算法                                         */
 3  //  時間複雜度T(n)=o(n)
 4  /************************************************************************/
 5 unsigned long long Fibonacci_bottom_up(int n)//自底向上算法
 6 {
 7     int result[2] = {0, 1};
 8     if(n < 2)
 9         return result[n];
10 
11     unsigned long long  fibNMinusN_1 = 1;
12     unsigned long long  fibNMinusN_2 = 0;
13     unsigned long long  fibN = 0;
14     for(int i = 2; i <= n; ++ i)   //從底到上逐次計算出數列值
15     {
16         fibN = fibNMinusN_1 + fibNMinusN_2;
17 
18         fibNMinusN_2 = fibNMinusN_1;
19         fibNMinusN_1 = fibN;
20     }
21 
22     return fibN;//返回數組值
23 }

 

      五、 遞歸平方算法(Recursive squaring)           

上面的算法已經能達到Θ(n)線性效率,然而下面的這個算法能使效率達到T(n) = Θ(lgn)。該算法是基於一個定理,定理以及證實過程以下圖所示。這樣,問題的求解即成爲了矩陣的乘方問題,算法效率因而提升到了Θ(lgn)。函數

矩陣求冪方法同常數求冪方法,採用分治思想,可參見常數求冪方法,下面是矩陣求冪實現代碼:測試

 1 /************************************************************************/
 2 /*  下面矩陣的n次冪,結果保存在      Result[2][2]                   */
 3 //   矩陣的n次冪,採用分治法,與x的n 次方算法一致,時間複雜度T(n)=o(logn)
 4 //    1    1
 5 //    1    0
 6 /************************************************************************/
 7 void Matrix_power(int n,unsigned long long Result[2][2])//求矩陣冪
 8 {
 9     unsigned long long AResult[2][2];
10     unsigned long long zResult1[2][2];
11     unsigned long long zResult2[2][2];
12 
13     AResult[0][0]=1;AResult[0][1]=1;AResult[1][0]=1;AResult[1][1]=0;
14     if (1==n)
15     {
16         Result[0][0]=1;Result[0][1]=1;Result[1][0]=1;Result[1][1]=0;
17     }
18     else if (n%2==0)
19     {
20         Matrix_power(n/2,zResult1);
21         MUL(zResult1,zResult1,Result);
22     }
23     else if (n%2 == 1)
24     {
25         Matrix_power((n-1)/2,zResult1);
26         MUL(zResult1,zResult1,zResult2);
27         MUL(zResult2,AResult,Result);
28     }
29 }

 

     其中,兩個矩陣相乘代碼實現以下:

 1 /************************************************************************/
 2 /* 兩個 矩陣相乘  、結果矩陣保存在 MatrixResult[2][2]中       */
 3 /************************************************************************/
 4 void MUL( unsigned long long MatrixA[2][2],unsigned long long MatrixB[2][2], unsigned long long MatrixResult[2][2] )//矩陣相乘
 5 {
 6     for (int i=0;i<2 ;i++)
 7     {
 8         for (int j=0;j<2 ;j++)
 9         {
10             MatrixResult[i][j]=0;
11             for (int k=0;k<2 ;k++)
12             {
13                 MatrixResult[i][j]=MatrixResult[i][j]+MatrixA[i][k]*MatrixB[k][j];
14             }
15         }
16     }
17 }

 

 

         六、完整代碼(c++)                                           

 Fibonacci.h   其中遞歸平方法(矩陣求冪法)有兩種實現,一種是我本身編碼實現,另外是網上copy的代碼。

  1 #ifndef FIBONACCI_HH
  2 #define FIBONACCI_HH
  3 struct Matrix2By2
  4 {
  5     Matrix2By2
  6         (
  7         unsigned long long m00 = 0, 
  8         unsigned long long m01 = 0, 
  9         unsigned long long m10 = 0, 
 10         unsigned long long m11 = 0
 11         )
 12         :m_00(m00), m_01(m01), m_10(m10), m_11(m11) 
 13     {
 14     }
 15 
 16     unsigned long long m_00;
 17     unsigned long long m_01;
 18     unsigned long long m_10;
 19     unsigned long long m_11;
 20 };
 21 class Fibonacci
 22 {
 23 public:
 24     unsigned long long Fibonacci_naive_reu(int n);//樸素遞歸算法
 25     unsigned long long Fibonacci_bottom_up(int n);//自底向上算法
 26     unsigned long long Fibonacci_recur_squar(int n);//遞歸平方算法
 27     void MUL( unsigned long long MatrixA[2][2],unsigned long long MatrixB[2][2], unsigned long long MatrixResult[2][2] );
 28     void  Matrix_power(int n,unsigned long long result[2][2]);
 29 
 30     Matrix2By2 MatrixMultiply(const Matrix2By2& matrix1, const Matrix2By2& matrix2);
 31     Matrix2By2 MatrixPower(unsigned int n);
 32     unsigned long long Fibonacci_Solution3(unsigned int n);
 33 
 34 };
 35 
 36  unsigned long long Fibonacci::Fibonacci_naive_reu(int n)//樸素遞歸算法
 37 {
 38     int result[2] = {0,1};
 39     if (n<2)       //退出條件
 40        return result[n];
 41 
 42     return (Fibonacci_naive_reu(n-1)+ Fibonacci_naive_reu(n-2));//遞歸調用
 43 }
 44  /************************************************************************/
 45  /*                         自底向上算法                                         */
 46  //  時間複雜度T(n)=o(n)
 47  /************************************************************************/
 48 unsigned long long Fibonacci::Fibonacci_bottom_up(int n)//自底向上算法
 49 {
 50     int result[2] = {0, 1};
 51     if(n < 2)
 52         return result[n];
 53 
 54     unsigned long long  fibNMinusN_1 = 1;
 55     unsigned long long  fibNMinusN_2 = 0;
 56     unsigned long long  fibN = 0;
 57     for(int i = 2; i <= n; ++ i)   //從底到上逐次計算出數列值
 58     {
 59         fibN = fibNMinusN_1 + fibNMinusN_2;
 60 
 61         fibNMinusN_2 = fibNMinusN_1;
 62         fibNMinusN_1 = fibN;
 63     }
 64 
 65     return fibN;//返回數組值
 66 }
 67 /************************************************************************/
 68 /* 兩個 矩陣相乘  、結果矩陣保存在 MatrixResult[2][2]中       */
 69 /************************************************************************/
 70 void Fibonacci::MUL( unsigned long long MatrixA[2][2],unsigned long long MatrixB[2][2], unsigned long long MatrixResult[2][2] )//矩陣相乘
 71 {
 72     for (int i=0;i<2 ;i++)
 73     {
 74         for (int j=0;j<2 ;j++)
 75         {
 76             MatrixResult[i][j]=0;
 77             for (int k=0;k<2 ;k++)
 78             {
 79                 MatrixResult[i][j]=MatrixResult[i][j]+MatrixA[i][k]*MatrixB[k][j];
 80             }
 81         }
 82     }
 83 }
 84 /************************************************************************/
 85 /*  下面矩陣的n次冪,結果保存在      Result[2][2]                   */
 86 //   矩陣的n次冪,採用分治法,與x的n 次方算法一致,時間複雜度T(n)=o(logn)
 87 //    1    1
 88 //    1    0
 89 /************************************************************************/
 90 void Fibonacci::Matrix_power(int n,unsigned long long Result[2][2])//求矩陣冪
 91 {
 92     unsigned long long AResult[2][2];
 93     unsigned long long zResult1[2][2];
 94     unsigned long long zResult2[2][2];
 95 
 96     AResult[0][0]=1;AResult[0][1]=1;AResult[1][0]=1;AResult[1][1]=0;
 97     if (1==n)
 98     {
 99         Result[0][0]=1;Result[0][1]=1;Result[1][0]=1;Result[1][1]=0;
100     }
101     else if (n%2==0)
102     {
103         Matrix_power(n/2,zResult1);
104         MUL(zResult1,zResult1,Result);
105     }
106     else if (n%2 == 1)
107     {
108         Matrix_power((n-1)/2,zResult1);
109         MUL(zResult1,zResult1,zResult2);
110         MUL(zResult2,AResult,Result);
111     }
112 }
113 unsigned long long Fibonacci::Fibonacci_recur_squar(int n)//遞歸平方算法
114 {
115     unsigned long long fib_result[2][2];
116     int result[2] = {0, 1};
117     if(n < 2)
118         return result[n];
119     Matrix_power(n,fib_result);
120     return fib_result[0][1];
121 }
122 
123 
124 
125 /************************************************************************/
126 /*     下面是網上copy的遞歸平方算法的代碼                          */
127 /************************************************************************/
128 ///////////////////////////////////////////////////////////////////////
129 // Multiply two matrices
130 // Input: matrix1 - the first matrix
131 //        matrix2 - the second matrix
132 //Output: the production of two matrices
133 ///////////////////////////////////////////////////////////////////////
134 Matrix2By2 Fibonacci::MatrixMultiply
135     (
136     const Matrix2By2& matrix1, 
137     const Matrix2By2& matrix2
138     )
139 {
140     return Matrix2By2(
141         matrix1.m_00 * matrix2.m_00 + matrix1.m_01 * matrix2.m_10,
142         matrix1.m_00 * matrix2.m_01 + matrix1.m_01 * matrix2.m_11,
143         matrix1.m_10 * matrix2.m_00 + matrix1.m_11 * matrix2.m_10,
144         matrix1.m_10 * matrix2.m_01 + matrix1.m_11 * matrix2.m_11);
145 }
146 
147 ///////////////////////////////////////////////////////////////////////
148 // The nth power of matrix 
149 // 1  1
150 // 1  0
151 ///////////////////////////////////////////////////////////////////////
152 Matrix2By2 Fibonacci::MatrixPower(unsigned int n)
153 {
154     Matrix2By2 matrix;
155     if(n == 1)
156     {
157         matrix = Matrix2By2(1, 1, 1, 0);
158     }
159     else if(n % 2 == 0)
160     {
161         matrix = MatrixPower(n / 2);
162         matrix = MatrixMultiply(matrix, matrix);
163     }
164     else if(n % 2 == 1)
165     {
166         matrix = MatrixPower((n - 1) / 2);
167         matrix = MatrixMultiply(matrix, matrix);
168         matrix = MatrixMultiply(matrix, Matrix2By2(1, 1, 1, 0));
169     }
170 
171     return matrix;
172 }
173 ///////////////////////////////////////////////////////////////////////
174 // Calculate the nth item of Fibonacci Series using devide and conquer
175 ///////////////////////////////////////////////////////////////////////
176 unsigned long long Fibonacci::Fibonacci_Solution3(unsigned int n)
177 {
178     int result[2] = {0, 1};
179     if(n < 2)
180         return result[n];
181 
182     Matrix2By2 PowerNMinus2 = MatrixPower(n - 1);
183     return PowerNMinus2.m_00;
184 }
185 #endif
Fibonacci.h

 

 

 

Fibonacci.cpp (主函數)

 1 #include <iostream>
 2 #include <vector>
 3 #include <ctime>
 4 using namespace std;
 5 #include "Fibonacci.h"
 6 
 7 
 8 int main()
 9 {
10     Fibonacci fib;
11     clock_t startTime_For_Fibonacci_naive_reu ;
12     clock_t endTime_For_Fibonacci_naive_reu ;
13     clock_t startTime_For_Fibonacci_bottom_up ;
14     clock_t endTime_For_Fibonacci_bottom_up ;
15     clock_t startTime_For_Fibonacci_recur_squar1 ;
16     clock_t endTime_For_Fibonacci_recur_squar1 ;
17     clock_t startTime_For_Fibonacci_recur_squar2 ;
18     clock_t endTime_For_Fibonacci_recur_squar2 ;
19 
20     startTime_For_Fibonacci_naive_reu=clock();
21     cout<<fib.Fibonacci_naive_reu(40)<<endl;//樸素遞歸算法
22     endTime_For_Fibonacci_naive_reu=clock();
23 
24     startTime_For_Fibonacci_bottom_up=clock();
25     cout<<fib.Fibonacci_bottom_up(40)<<endl;//自底向上算法
26     endTime_For_Fibonacci_bottom_up=clock();
27 
28     startTime_For_Fibonacci_recur_squar1=clock();
29     cout<<fib.Fibonacci_recur_squar(40)<<endl;//我本身編寫的遞歸平方算法
30     endTime_For_Fibonacci_recur_squar1=clock();
31 
32     startTime_For_Fibonacci_recur_squar2=clock();
33     cout<<fib.Fibonacci_Solution3(40)<<endl;//網上copy的遞歸平方算法
34     endTime_For_Fibonacci_recur_squar2=clock();
35 
36     cout<<"樸素遞歸算法用時: "<<(endTime_For_Fibonacci_naive_reu-startTime_For_Fibonacci_naive_reu)/(1.0*CLOCKS_PER_SEC)<<""<<endl;
37     cout<<"自底向上算法用時: "<<(endTime_For_Fibonacci_bottom_up-startTime_For_Fibonacci_bottom_up)/(1.0*CLOCKS_PER_SEC)<<""<<endl;
38     cout<<"我本身編寫的遞歸平方算法用時: "<<(endTime_For_Fibonacci_recur_squar1-startTime_For_Fibonacci_recur_squar1)/(1.0*CLOCKS_PER_SEC)<<""<<endl;
39     cout<<"網上copy的遞歸平方算法用時: "<<(endTime_For_Fibonacci_recur_squar2-startTime_For_Fibonacci_recur_squar2)/(1.0*CLOCKS_PER_SEC)<<""<<endl;
40     system("Pause");
41     return 0;
42 }

 

 

 

輸出:

結:樸素遞歸算法用時太多,實用價值不大,自底向上算法效率爲線性,較高,平時用較多,遞歸平方算法效率爲對數級,且編程可實現,實用價值很大。而且通過測試,當n值變很大後,遞歸平方算法效率明顯高於自底向上算法效率。

 

      七、參考資料

        【1】http://zhedahht.blog.163.com/blog/static/25411174200722991933440/

        【2】http://blog.csdn.net/xyd0512/article/details/8220506

相關文章
相關標籤/搜索