斐波那契數列
斐波那契數列指的是這樣一個數列:
$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