斐波那契数列
(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
一、循环法:
// n 太大会溢出,可以使用数据位更宽的数据类型
int fib(int n){
if(n==1||n==2) return 1;
int f1=1;
int f2=1;
int res=0; //存放结果
for(int i=3;i<=n;++i){
res=f1+f2;
f2=f1;
f1=res;
}
return res;
}
时间复杂度:O(n)
空间复杂度:O(1)
二、递归法:
根据递推公式:F(n)=F(n - 1)+F(n - 2)可直接得出
int fib(int n){
if(n<2) return n;
return fib(n-1)+fib(n-2);
}
时间复杂度:因为成二叉树形式 ,所以时间复杂度为O(2^n)
空间复杂度:树约有n-1层,所以空间复杂度为O(n)
三、动态规划法:
- 确定 dp数组以及下标的含义:dp[i] 的定义为:第 i 个数的斐波那契数值是 dp[i]
- 确定递推公式:题⽬已经把递推公式直接给我们了:状态转移⽅程 dp[i] = dp[i - 1] + dp[i - 2]
- 初始化dp数组: dp[0] = 0, dp[1] = 1
- 确定遍历顺序: dp[i] 是依赖 dp[i - 1] 和 dp[i - 2] ,所以遍历的顺序是从前到后遍历
int fib(int n){
if(n<2) return n;
int dp[n+1];
dp[0]=0;
dp[1]=1;
for(int i=2;i<=n;++i){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
时间复杂度:O(n)
空间复杂度:O(n)
根据斐波那契数列的性质,我们可以用 滚动数组来优化一下空间复杂度
int fib(int n){
if(n<2) return n;
int dp[2]={0,1};
int result=0;
for(int i=2;i<=n;++i){
result=dp[0]+dp[1];
dp[0]=dp[1];
dp[1]=result;
}
return dp[1];
}
时间复杂度:O(n)
空间复杂度:O(1)
方法四:快速矩阵幂
定义一个矩阵:
可以找到一个矩阵M(这里可以理解为从一个状态转移到另一个状态),使得:
设:
所以
由矩阵乘法可以得到:
af(n)+bf(n-1)=f(n+1)
cf(n)+df(n-1)=f(n)
最终求得:
由递推公式最终得到:
因此只要我们能快速计算矩阵 M 的 k 次幂,就可以得到 F(n)的值。
此处需要用到快速幂的算法,不明白的可以看一下快速幂算法_Sakeyuan的博客-CSDN博客:
//矩阵相乘
vector<vector<int>> array_multiply(vector<vector<int>>& a, vector<vector<int>>& b){
vector<vector<int>> c{ {0, 0}, {0, 0} };
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
vector<vector<int>> array_pow(vector<vector<int>>& a, int n) { //矩阵快速幂
vector<vector<int>> ret{ {1, 0}, {0, 1} }; //单位矩阵 ,储存结果
while (n > 0) {
if (n & 1) {
ret = array_multiply(ret, a);
}
n >>= 1;
a = array_multiply(a, a);
}
return ret;
}
int fib(int n) {
if (n < 2) {
return n;
}
vector<vector<int>> m{ {1, 1}, {1, 0} };
vector<vector<int>> result = array_pow(m, n);
return result[0][1]; // 由递推公式可以看出f(n)为 result[0][1]位置
}
时间复杂度:O(logn)
空间复杂度:O(1)
五、通项公式:
根据递推公式 F(n)=F(n - 1)+F(n - 2) 可以得出特征方程:
求得:
通解为:
带入f(0)=0,f(1)=1,解得:
最终得到通项公式:
int fib(int n) {
double sqrt_5 = sqrt(5);
double fib_n = pow((1 + sqrt_5) / 2, n) - pow((1 - sqrt_5) / 2, n);
return round(fib_n / sqrt_5); /*round()函数四舍五入*/
}