Fibonacci數列應該也算是耳熟能詳,它的遞歸定義如上圖所示。html
下面2-6分別說明求取Fibonacci數列的4種方法ios
在不少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增長,時間指數快速增長,因此,平時不多采用!算法
這個算法主要根據斐波那契數列的一條數學性質而來。該性質代表,斐波那契數列F(n)即爲φ^n / 5^½向下取整。這樣,問題的求解因而變成了一個求x的n次方的問題,在以前的文章裏有講解,連接請進。 因此算法的效率爲Θ(lgn)。可是這個方法是不太靠譜的,主要是當n比較大時,因爲硬件的限制計算機中的浮點運算獲得的結果與真實值就產生偏差了。實用價值也不大,只能用作理論分析。編程
考慮到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 }
上面的算法已經能達到Θ(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 }
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.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/