509.斐波那契数

509.斐波那契数

本题涉及内容:递归问题、动态规划问题、快速幂问题

题目链接https://leetcode-cn.com/problems/fibonacci-number/

递归

过于简单,直接看代码

int Fib(int n){
	if(n<=0)return 0;
	else if(n==1)return 1;
	else return Fib(n-1)+Fib(n-2);
}//这里定义Fib(0)=0,Fib(1)=1;后续项为前两项之和 

时间复杂度 O ( 2 n ) O(2^n) O(2n)

动态规划

注意自顶向下的方法对本题复杂度几乎没有任何优化,所以直接采用自底向上实现

int Fib(int n){
	if(n==0)return 0;
	else if(n==1)return 1;
	else{
		int p,q,i,tmp;
		for(p=0,q=1,i=2;i<=n;i++){
			tmp=q;
			q=p+q;
			p=tmp;
		}
		return q;
	}
}

Tips:动态规划尽量采用自底向上实现,不仅时间复杂度稳稳小于自顶向下,而且有利于重构解。(自顶向下的唯一一个有点就是思路比较符合正常逻辑)


下面的才是重点

基于矩阵乘法的快速幂实现

先了解一下快速幂算法

快速幂的基本原理,就是通过将 x n x^n xn利用减少乘法次数的方式,将时间复杂度由 O ( n ) O(n) O(n)降低到 O ( l o g ( n ) ) O(log(n)) O(log(n))

下面简单介绍一个相关的例子:

3 10 = 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 → ( 3 ∗ 3 ) ∗ ( 3 ∗ 3 ) ∗ ( 3 ∗ 3 ) ∗ ( 3 ∗ 3 ) ∗ ( 3 ∗ 3 ) → 9 ∗ 9 ∗ 9 ∗ 9 ∗ 9 → ( 9 ∗ 9 ) ∗ ( 9 ∗ 9 ) ∗ 9...... 3^{10}=3*3*3*3*3*3*3*3*3*3\rightarrow(3*3)*(3*3)*(3*3)*(3*3)*(3*3)\rightarrow9*9*9*9*9\rightarrow(9*9)*(9*9)*9...... 310=3333333333(33)(33)(33)(33)(33)99999(99)(99)9......
KaTeX parse error: Expected 'EOF', got '&' at position 28: …3*3*3*3*3*3*3*3&̲普通计算方法:乘法次数9\\3…

时间复杂度:快速幂乘法次数实际上就是固定的平方乘法+幂为奇数是的额外乘法(可以不考虑),所以总时间复杂度为 O ( l o g ( n ) ) O(log(n)) O(log(n))

快速幂算法实现

int Fast_Pow(int x,int n){//返回x的n次
	if(n==0)return 1;
	else if(n%2==0){
		int tmp=Fast_Pow(x,n/2);//注意保存tmp,这是优化关键
		return tmp*tmp;
	}
	else return x*Fast_Pow(x,n-1));
}

矩阵乘法

通过如下转化图,可以看出矩阵求幂与斐波那契数列之间的关系
[ 1 1 1 0 ] [ F i b ( n − 1 ) F i b ( n − 2 ) ] = [ F i b ( n − 1 ) + F i b ( n − 2 ) F i b ( n − 1 ) ] = [ F i b ( n ) F i b ( n − 1 ) ] \begin{bmatrix}1&1\\1&0\end{bmatrix}\begin{bmatrix}Fib(n-1)\\Fib(n-2)\end{bmatrix}=\begin{bmatrix}Fib(n-1)+Fib(n-2)\\Fib(n-1)\end{bmatrix}=\begin{bmatrix}Fib(n)\\Fib(n-1)\end{bmatrix} [1110][Fib(n1)Fib(n2)]=[Fib(n1)+Fib(n2)Fib(n1)]=[Fib(n)Fib(n1)]
可见其中的递归性质,由此可以总结得到公式
F i b ( n ) = [ 1 1 1 0 ] n − 1 [ F i b ( 1 ) F i b ( 0 ) ] Fib(n)=\begin{bmatrix}1&1\\1&0\end{bmatrix}^{n-1}\begin{bmatrix}Fib(1)\\Fib(0)\end{bmatrix} Fib(n)=[1110]n1[Fib(1)Fib(0)]
同理,那么3位的泰波那契数该怎么求呢 T r i b ( n ) = T r i b ( n − 1 ) + T r i b ( n − 2 ) + T r i b ( n − 3 ) Trib(n)=Trib(n-1)+Trib(n-2)+Trib(n-3) Trib(n)=Trib(n1)+Trib(n2)+Trib(n3)
? ∗ [ T r i b ( n − 1 ) T r i b ( n − 2 ) T r i b ( n − 3 ) ] − > [ T r i b ( n ) T r i b ( n − 1 ) T r i b ( n − 2 ) ] ? = [ 1 1 1 1 0 0 0 1 0 ] ?*\begin{bmatrix}Trib(n-1)\\Trib(n-2)\\Trib(n-3)\end{bmatrix}->\begin{bmatrix}Trib(n)\\Trib(n-1)\\Trib(n-2)\end{bmatrix}\\?=\begin{bmatrix}1&1&1\\1&0&0\\0&1&0\end{bmatrix} ?Trib(n1)Trib(n2)Trib(n3)>Trib(n)Trib(n1)Trib(n2)?=110101100

所以只需将快速幂的算法带入矩阵乘法即可

int **Matrix_Mul(int **m1,int **m2){//2*2矩阵乘法
	int **res=(int**)calloc(sizeof(int**),2);
	res[0]=(int*)calloc(sizeof(int),2);
	res[1]=(int*)calloc(sizeof(int),2);
	res[0][0]=m1[0][0]*m2[0][0]+m1[0][1]*m2[1][0];
	res[0][1]=m1[0][0]*m2[0][1]+m1[0][1]*m2[1][1];
	res[1][0]=m1[1][0]*m2[0][0]+m1[1][1]*m2[1][0];
	res[1][1]=m1[1][0]*m2[0][1]+m1[1][1]*m2[1][1];
	return res;
}
int** Fast_Pow(int **x,int n){//返回x的n次
	if(n==1)return x;
	else if(n%2==0){
		int **tmp=Fast_Pow(x,n/2);
		return Matrix_Mul(tmp,tmp);
	}
	else return Matrix_Mul(x,Fast_Pow(x,n-1));
}
int Fib(int n){
	if(n==0)return 0;
	if(n==1)return 1;
	int m=n-1;
	int **x=(int**)calloc(sizeof(int**),2);
	x[0]=(int*)calloc(sizeof(int),2);
	x[1]=(int*)calloc(sizeof(int),2);
	x[0][0]=x[1][0]=x[0][1]=1;x[1][1]=0;
	int **res=Fast_Power(x,m);
	return res[0][0];
}//这里定义Fib(0)=0,Fib(1)=1;后续项为前两项之和 

最后附上运行时间的比较

小结

快速幂算法其实还能做一些小优化,之后的博客里会讲到

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值