快速幂(整数快速幂+矩阵快速幂)

闲的无聊,总结一下快速幂的算法,一直在用偏移运算符,现在试试多种方法:

快速幂就是求一个数的m次方的快速求法,因为正常情况下求次方可能就是一个数循环m次,然后乘一下,低次幂还好有一点,但是碰见高次的就不好办了,比如:

求2的10次方,这个很好办,for循环10次:

#include<stdio.h>            //可以,很简单
int main()
{
	int i,mi=1;
	for(i=0;i<10;++i)
		mi=mi*2;
	printf("%d\n",mi);
}

但是碰见10000000次呢?比如2的100000000次幂怎么办。。循环肯定超时,因此我们可以曲线救国,运用快速幂比如:

原理:任何一个数都可以用二进制进行表达,我们给他拆开看,一次判断一位:

比如2的11次方:11用二进制就是1011,分别是8*1+4*0+2*1+1*1,因此我们只用循环四次就好了,

四次的循环:

 第一次:0+1*1
第二次:1+2*1
第三次:3+4*0
第四次:3+8*1 

而不用循环11次,再举个例子:2的100000次方:100000用二进制表示是1 1000 0110 1010 0000,就是1*0+2*0+4*0+8*0+16*0+32*1+64*0+128*1+。。。

循环17次就行了,看100000次的循环缩减到了17次,是不是很好用?它是按照倍数来加的,因此就算爆int的数也只用32次就行了,感觉贼快,好了,废话就不说了,我们为了实现这种方法,就要每次更换底数,也就是说每次的下面的数是上一次的平方。。。应该还好吧。

上代码。。。:(偏移运算符法)

#include<stdio.h>
#include<string.h>
#include<math.h>

#include<map>
#include<queue>
#include<stack>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;

#define ll long long
#define da    0x3f3f3f3f
#define xiao -0x3f3f3f3f
#define clean(a,b) memset(a,b,sizeof(a))//以上是雷打不动的头文件23333 

int quick(int a,int n)		//快速幂 
{
	int can=a;			// 要求得底数 
	int ji=1;			//乘积 
	while(n)			//还有次幂 
	{
		if(n&1)
			ji=ji*can;	//如果这个地方是1,就乘上去,如果是0就不用乘 	//取模防爆int比如: ji=ji*can%1000000;
		can=can*can;		//每次的底数变平方 							//同理取模 			can=can*can%100000;
		n=n>>1;				//相当于每次右偏一次,每次除2 
	}
	return ji;
}

int main()
{
	int n,a;
	scanf("%d%d",&a,&n);	//a是底数,n是次幂 
	printf("%d\n",quick(a,n));//这样如果数大了必定爆int,因此我们一般都取一下模,例如%100000 
	
}

矩阵快速幂:

与整数快速幂方法类似,只不过整数部分换成了矩阵相乘

实现代码:

#include<stdio.h>
#include<string.h>  
#include<math.h>  
  
#include<map>   
//#include<set>
#include<deque>  
#include<queue>  
#include<stack>  
#include<bitset> 
#include<string>  
#include<iostream>  
#include<algorithm>  
using namespace std;  

#define ll long long  
#define INF 0x3f3f3f3f  
#define mod 1000000007
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b) 
#define clean(a,b) memset(a,b,sizeof(a))// 水印 
//std::ios::sync_with_stdio(false);

ll temp[3][3],res[3][3],ans[3][3];

void matrix(ll a[][3],ll b[][3])
{
	clean(temp,0);//加一个中间暂时存放数据的矩阵
	for(int i=1;i<3;++i)
	{
		for(int j=1;j<3;++j)
		{
			for(int k=1;k<3;++k)
				temp[i][j]=(temp[i][j]+a[i][k] * b[k][j])%mod;//矩阵相乘
		}
	}
	
	for(int i=1;i<3;++i)
	{
		for(int j=1;j<3;++j)
			a[i][j]=temp[i][j]%mod;//矩阵重新赋值
	}
}

void quick(ll n)
{
	/*
	矩阵快速幂
	[a,b,c,d] [a,b,c,d];
	[1,1,1,0] [1,1,1,0];
	[a*a+b*c , a*b+b*d , a*c+c*d , b*c+d*d]
	[1+1,1+0,1+0,1+0]  
	
	*/
	clean(ans,0);
	clean(res,0);
	res[1][1]=1;//初始矩阵,从三开始
	res[1][2]=1;
	res[2][1]=1;
	res[2][2]=0;
	for(int i=1;i<3;++i)//单位矩阵
	{
		for(int j=1;j<3;++j)
		{
			if(i==j)
				ans[i][j]=1;
			else
				ans[i][j]=0;
		}
	}
	//矩阵初始化
	while(n)
	{
		if(n&1)
			matrix(ans,res);//乘积
		matrix(res,res);
		n=n>>1;
	}
}

int main()
{
	ll n;
	cin>>n;
	for(int i=0;i<n;++i)
	{
		ll n;
        cin>>n;
        quick(n-2);//
        cout<<(ans[1][1]+ans[2][1])%mod<<endl;
		
		
	}
}

 

先就这了,后面的会不定期补更。。。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值