斐波拉契数列-矩阵快速幂算法

参考学习:

https://blog.csdn.net/acecandy/article/details/83864763

斐波拉契数列 矩阵快速幂

题目描述

PIPI今天刚刚学习了斐波拉契数列:
F(0) = 1
F(1) = 1
F(n) = F(n-1) + F(n-2) (n>1)
现在PIPI想知道斐波拉契数列的第 k 项,胖虎能够告诉PIPI吗?
由于结果很大,输出F(k)对p取模的结果即可~
p = 109+7

输入

输入包含多组测试用例。
每组测试用例包含一个正整数 k (1<=k<=1010)

输出

对于每组样例,输出F(k)%p

样例输入

1
2
3
4
5

样例输出

1
2
3
5
8

 

\binom{fn}{fn-1} =\begin{pmatrix} 1 &1 \\ 1&0 \end{pmatrix} \binom{fn-1}{fn-2}=\begin{pmatrix} 1 & 1\\ 1& 0 \end{pmatrix}^{2}\binom{fn-2}{fn-3}=\begin{pmatrix} 1 &1 \\ 1&0 \end{pmatrix}^{n-1}\binom{f1}{f0}

默认初始矩阵单位阵:

\begin{pmatrix} 1&0 \\ 0&1 \end{pmatrix}   

1次幂矩阵:

\begin{pmatrix} 1 &1 \\ 1&0 \end{pmatrix}

通过计算幂矩阵的n-1次,从而得出fn的值。

m^{n}这种问题,当然不是一直for循环乘上去这么蠢的方法。
假如我们需要求m^{63},可以把63分解为32+16+8+4+2+1。m^{63} = m^{1}+m^{2}+m^{4}+m^{8}+m^{16}+m^{32}
for累加需要计算62次乘法,而2进制分解只要6次乘法,5次加法。
不管指数是多少,都可以将其分解为 2 的倍数的和。因为任何整数都能够写成 2 进制的形式,2进制可以表示任何一个整数。这就是关于求次方的快速幂算法。

如15 = 1111(2),求m^{15}。  sum =1, an=m; 记x=15

第一步,1111&1 =1(真,按位与) sum=sum*an=m, 1111>>1(左移1位,除2),an = m*m;   x=111(2) =7

第二步,111&1 =1, sum=sum*an = m^{3}, 111>>1, an =m^{2}*m^{2}=m^{4}, x=11(2) =3

第三步,11&1=1,sum =sum*an = m^{7}, 11>>1,an =m^{4}*m^{4}=m^{8},x=1(2)=1

第三步,1&1=1,sum = sum*an = m^{15},1>>1,x=0,退出循环。即求出m^{15}

m^{15} = m^{1}+m^{2}+m^{4}+m^{8}

参考代码如下:

#include<iostream>
#include<cstring>
using namespace std;
#define p 1000000007

int main(){
	long long  k;
	while(scanf("%d",&k)!=EOF){
		if(k==0 || k==1)
		{
			printf("1\n");	
			continue;
		}
		long long mar[2][2] ={1,1,1,0};  //1次幂 
		long long res[2][2] ={1,0,0,1}; //单位阵 
        k = k-1;
		while(k>0){
			if(k&1){
				long long temp[2][2];
				temp[0][0] = (res[0][0]*mar[0][0] +res[0][1] *mar[1][0])%p;
				temp[0][1] = (res[0][0]*mar[0][1] +res[0][1] *mar[1][1])%p;
				temp[1][0] = (res[1][0]*mar[0][0] +res[1][1] *mar[1][0])%p;
				temp[1][1] = (res[1][0]*mar[0][1] +res[1][1] *mar[1][1])%p;
				memcpy(res,temp,sizeof(temp));
			}
			k = k>>1;
			long long temp[2][2]; 
			temp[0][0] = (mar[0][0]*mar[0][0] +mar[0][1] *mar[1][0])%p;
			temp[0][1] = (mar[0][0]*mar[0][1] +mar[0][1] *mar[1][1])%p;
			temp[1][0] = (mar[1][0]*mar[0][0] +mar[1][1] *mar[1][0])%p;
			temp[1][1] = (mar[1][0]*mar[0][1] +mar[1][1] *mar[1][1])%p;
			memcpy(mar,temp,sizeof(temp));	
		}
		long long sum;
		sum = res[0][0] + res[0][1];
		printf("%lld\n",sum%p);
	} 
	
	return 0;
} 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值