关于斐波那契数列

FZQOJ 博客链接

博客园链接

关于斐波那契数列

警告:本文使用了微少的 LATEX,请谨慎食用。

斐波那契数列的定义

F(n)={0n=01n=1F(n1)+F(n2)n2

这个是很显然的吧。

例题

P1720 月落乌啼算钱(斐波那契数列)

这一道题是语法的入门题,由于项数不超过 33,我们可以直接使用递推公式。

#include<cstdio>
int main(){
	int n,n1=1,n2=1,s=0;//第一项和第二项。
	scanf("%d",&n);
	if (n==1) return printf("%d\n",n1),0;
	s=n2;
	for (int i=3;i<n;i++){
		if (i&1) n1+=n2,s=n1;
		else n2+=n1,s=n2;
	} 
	printf("%d\n",s);
    return 0;
}

上面的代码时间复杂度是 O(n) 的,但是,斐波那契数列是有公式的!我们不能只止步于递推。

公式:

F(n)=15[(1+52)n(152)n]

证明(虽然我也没看懂):

斐波那契通项公式证明.png

于是我们就可以利用上面的通项公式,使用 cmath 库中的 sqrtpow 函数来简洁地求解:

//the code is from chenjh
#include<cstdio>
#include<cmath>
int main(){
	int n;scanf("%d",&n);
	printf("%.2lf\n",1.0/sqrt(5)*(pow((1+sqrt(5))/2,n)-pow((1-sqrt(5))/2,n)));
	return 0;
}

上面的代码的时间复杂度是 O(1) 的!(我们可以暂且认为 sqrtpow 函数的时间复杂度 O(1) 的)

「POJ3070」Fibonacci

此题要求 Fn(0n109),普通的 O(n) 递推一定是无法胜任的。

所以我们就需要——矩阵乘法来加速递推。

斐波那契数列的另外一个通项公式是:

[Fn+1FnFnFn1]=[1110]n=[1110][1110][1110]n times

于是我们就可以利用矩阵乘法配合快速幂进行 O(23logn) 复杂度的递推,其中 23 为矩阵乘法所需要的复杂度。

//the code is from chenjh
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int m=10000;
void mul(int f[2],int a[2][2]){
	int c[2];
	memset(c,0,sizeof(c));
	for(int j=0;j<2;j++)
		for(int k=0;k<2;k++)
			c[j]=(c[j]+(LL)f[k]*a[k][j])%m;
	memcpy(f,c,sizeof(c));
}
void mulself(int a[2][2]){
	int c[2][2];
	memset(c,0,sizeof(c));
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			for(int k=0;k<2;k++)
				c[i][j]=(c[i][j]+(LL)a[i][k]*a[k][j])%m;
	memcpy(a,c,sizeof(c));
}
int main(){
	int n;
	while(cin>>n && n!=-1){
		int f[2]={0,1};
		int a[2][2]={{0,1},{1,1}};
		for(;n;n>>=1){
			if(n&1) mul(f,a);
			mulself(a);
		}
		cout<<f[0]<<endl;
	}
	return 0;
}

LibreOJ #10221. 「一本通 6.5 例 3」Fibonacci 前 n 项和

设斐波那契数列的第 n 项为 Fn,前 n 项和为 Sn

方法一

根据矩阵乘法的定义,可以列出公式:

[SnFnFn+1]=[Sn1Fn1Fn]×[100001111]

最后利用矩阵乘法配合快速幂实现,时间复杂度为 O(33logn)

//the code is from chenjh
#include<cstdio>
#include<cstring>
typedef long long LL;
LL m;
void mul(LL f[3],LL a[3][3]){
	LL c[3];
	memset(c,0,sizeof(c));
	for(int j=0;j<3;j++)
		for(int k=0;k<3;k++)
			c[j]=(c[j]+f[k]*a[k][j])%m;
	memcpy(f,c,sizeof(c));
}
void mulself(LL a[3][3]){
	LL c[3][3];
	memset(c,0,sizeof(c));
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
			for(int k=0;k<3;k++)
				c[i][j]=(c[i][j]+a[i][k]*a[k][j])%m;
	memcpy(a,c,sizeof(c));
}
int main(){
	LL n;
	scanf("%lld%lld",&n,&m);
	LL f[3]={0,0,1};
	LL a[3][3]={{1,0,0},{0,0,1},{1,1,1}};
	for(;n;n>>=1){
		if(n&1) mul(f,a);
		mulself(a);
	}
	printf("%lld\n",f[0]);
	return 0;
}

方法二

公式:

Sn=Fn+21

证明:

运用数学归纳法。

n=1 时,命题成立。

假设 n=k 时,命题成立。

n=k+1 时:

Fk+31=Fk+1+Fk+21=Fk+1+i=1kFi=i=1k+1Fi

所以直接用矩阵乘法配合快速幂求出 Fn+2 即可。

时间复杂度 O(23logn)

//the code is from chenjh
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL m;
void mul(LL f[2],LL a[2][2]){
	LL c[2];
	memset(c,0,sizeof(c));
	for(int j=0;j<2;j++)
		for(int k=0;k<2;k++)
			c[j]=(c[j]+f[k]*a[k][j])%m;
	memcpy(f,c,sizeof(c));
}
void mulself(LL a[2][2]){
	LL c[2][2];
	memset(c,0,sizeof(c));
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			for(int k=0;k<2;k++)
				c[i][j]=(c[i][j]+a[i][k]*a[k][j])%m;
	memcpy(a,c,sizeof(c));
}
int main(){
	LL n;
	scanf("%lld%lld",&n,&m);
	n+=2;
	LL f[2]={0,1};
	LL a[2][2]={{0,1},{1,1}};
	for(;n;n>>=1){
		if(n&1) mul(f,a);
		mulself(a);
	}
	printf("%lld\n",f[0]-1);
	return 0;
}

LibreOJ #10222. 「一本通 6.5 例 4」佳佳的 Fibonacci

计算出前 n 项,输入到 OEIS 中,你会发现就是 A014286 号数列。

通项公式是:

S(n)=n×Fn+2Fn+3+2

于是就可以直接计算了,但是我们仍要证明一下:

把他变成一个三角形:

F1F2F2F3F3F3F4F4F4F4F5F5F5F5F5

答案就是这个三角形内所有数之和。

我们可以把这个三角形竖着看,发现了啥?这个式子就是:

i=1n(SnSi1)

Sn 提取出来:

nSni=1nSi1

这个时候,Sn=Fn+21 派上用场了。把这个式子往里面套啊!

n(Fn+21)i=1n(Fi+11)

发现了啥?i=1n(Fi+11) 其实也可以转化为 Sn+1F1n

Sn=Fn+21 再往 Sn+1 里面套!

答案就变成了:

n(Fn+21)(Fn+31F1n)=nFn+2nFn+3+1+F1+n=nFn+2Fn+3+2

//the code is from chenjh
#include<cstdio>
#include<cstring>
#define rep(i,a,b) for(int i=(a);i<(b);++i)
typedef long long LL;
int n,m;
void mul(int f[2],int a[2][2]){
	int c[2];
	memset(c,0,sizeof c);
	rep(i,0,2)rep(j,0,2)c[i]=(c[i]+(LL)f[j]*a[j][i])%m;
	memcpy(f,c,sizeof c);
}
void mulself(int a[2][2]){
	int c[2][2];
	memset(c,0,sizeof c);
	rep(i,0,2)rep(j,0,2)rep(k,0,2)c[i][j]=(c[i][j]+(LL)a[i][k]*a[k][j])%m;
	memcpy(a,c,sizeof c);
}
int main(){
	scanf("%d%d",&n,&m);
	int f[2]={0,1};
	int a[2][2]={{0,1},{1,1}};
	for(int x=n+2;x;x>>=1,mulself(a))if(x&1)mul(f,a);
	printf("%lld\n",((LL)n*f[0]-f[1]+2+m)%m);
	return 0;
}

补充知识

  1. fib在模m后,一定存在周期性。
  2. More:斐波那契数列的性质 - Milkor - 博客园 (cnblogs.com)

参考文献

本人制作不易,公式可能会有打错的地方,欢迎指出错误,点个赞吧

posted @   Chen_Jinhui  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话

一言

你还是…笑起来…最棒了……
——最终幻想XIV
点击右上角即可分享
微信分享提示