蓝桥杯 入门训练之Fibonacci数列

Fibonacci数列

资源限制
时间限制:1.0 s 内存限制:256.0 MB

问题描述
Fibonacci数列的递推公式为: F n = F n 1 + F n 2 F_n = F_{n-1} + F_{n-2} ,其中 F 1 = F 2 = 1 F_1=F_2=1
当n比较大时, F n F_n 也非常大,现在我们想知道, F n F_n 除以10007的余数是多少。

输入格式
输入包含一个整数n

输出格式
输出一行,包含一个整数,表示 F n F_n 除以10007的余数。

说明:在本题中,答案是要求 F n F_n 除以10007的余数,因此我们只要能算出这个余数即可,而不需要先计算出 F n F_n 的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数再取余简单。

样例输入
10

样例输出
55

样例输入
22

样例输出
7704

数据规模
1 < = n < = 1 , 000 , 000 1<=n<=1,000,000

解题思路:

第一反应是递归,结果发现自己有点憨憨递归肯定超时,结果真的30分,于是就想着优化 ,我会的有下面两种方式

  • 利用类似于vector设置两个变量一个中间变量: a , b = b , ( a + b ) % 10007 a,b = b, (a+b)\%10007 即可
  • 利用曾经学过的矩阵快速幂加快运算,这里手推了一下又参考了网上的模板

提交代码-C++:

方案一:使用for循环
// 正常的 for 循环
#include<iostream>
using namespace std;

int main() {
	int a=1,b=1,n;
	int t;
	cin >> n;
	
	if (n==1 || n==2) cout<<1<<endl;
	else{
		for (int i=2;i<n;i++){
			t = a;
			a = b;
			b = (b+t)%10007;
		}	
		cout<<(b%10007)<<endl;
	}
	return 0;
}

方案一 :使用vector
#include<iostream>
#include<vector>
using namespace std;

int main(){
	int n;
	cin>>n;
	vector <int> lst;
	lst.push_back(1);
	lst.push_back(1);

	for (int i=2;i<n;i++){
		lst.push_back((lst.at(lst.size()-1)+lst.at(lst.size()-2))%10007);	
	}
	cout<<lst.at(lst.size()-1)<<endl;
	return 0;
}


方案二:

由于 F i b o n a c c i Fibonacci 数列的特殊性,公式为: F n = F n 1 + F n 2 F_n = F_{n-1} + F_{n-2} ,其中 F 1 = F 2 = 1 F_1=F_2=1 ,所以有如下矩阵关系:
[ f ( n ) f ( n 1 ) ] = [ 1 1 1 0 ] [ f ( n 1 ) f ( n 2 ) ] (1) \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} \tag{1}

f ( n ) = f ( n 1 ) + f ( n 2 ) (2) f(n) = f(n-1)+f(n-2) \tag{2}

f ( n 1 ) = f ( n 1 ) (3) f(n-1)=f(n-1) \tag{3}

A ( n ) = T A ( n 1 ) (4) 矩阵关系为:A(n) = T*A(n-1) \tag{4}

同理, f n + 1 f n f_{n+1}和f_n 都可以通过继续乘以一个矩阵 A n = [ 1 1 1 0 ] A_n=\begin{bmatrix} 1 &1 \\1 &0\end{bmatrix} 得到,所以根据所学矩阵乘法计算 [ f ( n ) f ( n 1 ) ] \begin{bmatrix} f(n) \\ f(n-1) \end{bmatrix} 的幂方,可以根据矩阵 A n {A}^n 的幂方得到。

A n = T n 1 A 1 A_n = T^{n-1} * A_1 这里的转移矩阵就是 [ 1 1 1 0 ] \begin{bmatrix} 1 &1 \\1 &0\end{bmatrix} A 1 A_1 就是初始矩阵,而 A n A_n 则是乘以 n 1 n-1 次的常数矩阵得到的结果

// 试题 入门训练 Fibonacci数列

#include<iostream>
#include<vector>
#include<algorithm>
#include<string.h>
using namespace std;

const int N=2;
const int mod=10007;

int tmp[N][N],res[N][N];

// temp记录矩阵a和矩阵b的乘积结果 ,这里的b矩阵就是常数/转移矩阵 
void multi(int a[][N],int b[][N],int N){ 
	memset(tmp,0,sizeof(tmp));	
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
				for(int k=0;k<N;k++){
						tmp[i][j]+=(a[i][k]*b[k][j])%mod; // 代表tmp[i][j]为A的第i行和B的第j列所有数 
				}
				 tmp[i][j]=tmp[i][j]%mod;
		}
	}
	
	//更新A矩阵,为下一次使用做准备 
	for(int i=0;i<N;i++)
	for(int j=0;j<N;j++)
	a[i][j]=tmp[i][j];
	 
}

//n是幂次数,N是矩阵大小,矩阵a代表的就是幂矩阵,将整数快速幂的整数换成矩阵即可 
void Pow(int a[][N],int n){
	memset(res, 0, sizeof res);
	for (int i=0;i<N;i++) res[i][i]=1; //单位矩阵初始化,对角线值为1 
	while (n) {
		if (n&1)
			multi(res,a,N);//res=res*a;复制直接在multi里面实现了;
        multi(a,a,N);//a=a*a
        n>>=1;
	}
}

// 矩阵快速幂
int main(){

	int n;
	cin>>n;
	int a[N][N]={{1,1},{1,0}}; 

	Pow(a,n);
	cout << res[0][1]<<endl; 
	
	return 0;
} 

参考文章/博客:

这里的文章主要是对快速幂和矩阵乘法下的快速幂进行了讲解了,有利于对这个点的认识

快速幂

  1. https://www.cnblogs.com/yewanting/p/10743018.html
  2. https://blog.csdn.net/Harington/article/details/87602682

矩阵快速幂

  1. https://blog.csdn.net/zhangxiaoduoduo/article/details/81807338
  2. https://blog.csdn.net/qq_42817826/article/details/81566544
  3. https://www.cnblogs.com/cmmdc/p/6936196.html
总结

虽然是个水题,但是第一次的博客,在不断的学习/修改快速幂和矩阵快速幂部分内容,不仅是为了自己的更好理解,也是希望大家在浏览的时候能给出意见,指出不足

发布了9 篇原创文章 · 获赞 1 · 访问量 90

猜你喜欢

转载自blog.csdn.net/Lurker_hunter/article/details/104549360