斐波那契数列的5种算法

斐波那契数列(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)(≥ 2,∈ 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 cur;	
}

时间复杂度: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)

三、动态规划法:

1. 确定 dp数组以及下标的含义:dp[i] 的定义为:第 i 个数的斐波那契数值是 dp[i]
2. 确定递推公式:题⽬已经把递推公式直接给我们了:状态转移⽅程 dp[i] = dp[i - 1] + dp[i - 2]
3. 初始化dp数组: dp[0] = 0, dp[1] = 1
4. 确定遍历顺序: 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)
 

方法四:快速矩阵幂

定义一个矩阵:
      

\begin{bmatrix} f(n)\\f(n-1) \end{bmatrix}

可以找到一个矩阵M(这里可以理解为从一个状态转移到另一个状态),使得:
   

M\begin{bmatrix} f(n)\\f(n-1) \end{bmatrix}=\begin{bmatrix} f(n+1)\\f(n) \end{bmatrix}

 设:    M=\begin{bmatrix} a &b \\ c&d \end{bmatrix}, 所以            

\begin{bmatrix} a &b \\ c&d \end{bmatrix}*\begin{bmatrix} f(n)\\f(n-1) \end{bmatrix}=\begin{bmatrix} f(n+1)\\f(n) \end{bmatrix}

 由矩阵乘法可以得到:

af(n)+bf(n-1)=f(n+1)

cf(n)+df(n-1)=f(n)

最终求得:
                                                     

M=\begin{bmatrix} 1 &1 \\1 & 0 \end{bmatrix}

由递推公式最终得到:

\begin{bmatrix} f(n+1)\\f(n) \end{bmatrix}=\begin{bmatrix} 1 &1 \\ 1 &0 \end{bmatrix}^k\begin{bmatrix} f(1)\\f(0) \end{bmatrix}

  

因此只要我们能快速计算矩阵 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) 可以得出特征方程:

x^2=x+1

求得:

x{_{1}}=\frac{1+\sqrt{5}}{2},x{_{2}}=\frac{1-\sqrt{5}}{2}

通解为:

f(n)=c_{1}x_{1}^n+c_{2}x_{2}^n

带入f(0)=0,f(1)=1,解得:

c{_{1}}=\frac{1}{\sqrt{5}},c{_{2}}=-\frac{1}{\sqrt{5}}

最终得到通项公式:

f(n)=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^n,(\frac{1-\sqrt{5}}{2})^n]

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()函数四舍五入*/
    }

  • 67
    点赞
  • 423
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓晓知道了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值