0 递归
斐波那契数列定义:
$F(n)=\left\{\begin{matrix}
0, & n=0\\
1, & n=1\\
F(n-1)+F(n-2), & n>1
\end{matrix}\right.$
递归解法最直观,但是复杂度也最高:$O(2^n)$
1 int Fibonacci(int n) 2 { 3 if (n <= 0) //细节可以处理非法输入 4 return 0; 5 else if (1 == n) 6 return 1; 7 return Fibonacci(n - 1) + Fibonacci(n - 2); 8 }
为了避免重复计算,可以将每一步计算得到的$F(i)$存起来,这样的话时间复杂度降为$O(n)$,但空间复杂度升为$O(n)$。
1 通项
求解通项的方法有好几种,下面展示一种用线性代数求解的方法:
对于线性递推数列,通项公式可以表示为$F(n)=C_1\lambda_1^n+C_2\lambda_2^n$。
根据斐波那契数列的递推公式,可得其特征方程:
$x^2=x+1$
解之得特征值:$\lambda_1=\frac{1+\sqrt5}{2},\lambda_2=\frac{1-\sqrt5}{2}$
将特征值带入$F(0), F(1)$,解得$C_1=-\frac{1}{\sqrt5}, C_2=\frac{1}{\sqrt5}$
故斐波那契数列的通项公式为:
$F(n)=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n]$
用公式求解的复杂度为$O(1)$,但是由于无理数在计算机中的存储不是精确的,所以结果的精度很难保证。
2 分治
通过矩阵形式的递推:
$$\begin{bmatrix}
F(n)\\
F(n-1)
\end{bmatrix}=\begin{bmatrix}
1 & 1\\
1 & 0
\end{bmatrix}\begin{bmatrix}
F(n-1)\\
F(n-2)
\end{bmatrix}$$
不断向下递推,可以得到:
$$\begin{bmatrix}
F(n)\\
F(n-1)
\end{bmatrix}={\begin{bmatrix}
1 & 1\\
1 & 0
\end{bmatrix}}^{n-1}\begin{bmatrix}
F(1)\\
F(0)
\end{bmatrix}$$
接下来就是求解矩阵的高次方,通过快速幂(https://baike.baidu.com/item/快速幂/5500243?fr=aladdin)可以在$O(logn)$时间内进行计算:
整数的快速幂代码:
1 int QuickPow(int a,int n) 2 { 3 int ans = 1; 4 while (n) 5 { 6 if (n & 1) 7 ans *= a; 8 a *= a; 9 n >>= 1; 10 } 11 12 return ans; 13 }
将传入的参数改为矩阵,乘法改为矩阵乘法,就可以得到矩阵快速幂:
以二阶矩阵为例,求解斐波那契数列:
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <iostream> 4 5 using namespace std; 6 7 struct Matrix { 8 int a[2][2]; 9 }base,ans; 10 11 Matrix multi(Matrix a, Matrix b) 12 { 13 Matrix res; 14 for (int i = 0; i < 2; i++) //第i行 15 { 16 for (int j = 0; j < 2; j++) //第j列 17 { 18 res.a[i][j] = 0; 19 for (int k = 0; k < 2; k++) 20 res.a[i][j] += a.a[i][k] * b.a[k][j]; 21 } 22 } 23 24 return res; 25 } 26 27 Matrix QuickPow(int n) 28 { 29 base.a[0][0] = base.a[0][1] = base.a[1][0] = 1; 30 base.a[1][1] = 0; //初始化矩阵 31 32 //结果矩阵初始化为单位阵 33 ans.a[0][0] = ans.a[1][1] = 1; 34 ans.a[1][0] = ans.a[0][1] = 0; 35 36 while (n) 37 { 38 if (n & 1) 39 { 40 ans = multi(ans, base); 41 } 42 base = multi(base, base); 43 n >>= 1; 44 } 45 46 return ans; 47 } 48 49 int main() 50 { 51 int n; 52 cin >> n; 53 54 QuickPow(n); 55 cout << ans.a[1][0] << endl; 56 57 return 0; 58 }