快速幂,十进制快速幂,矩阵快速幂

之前暑假写过矩阵快速幂的题,但是很遗憾,自己当时并没有弄明白这个快速幂具体事怎么实现的,现在的话,自己就想把有关快速幂的模板做一个整理,也是对自己学过的知识的总结吧。
首先的话 快速幂,想到的肯定是二进制快速幂
之前看过一个博客上面写的我感觉总结的很到位:
快速幂就是 增大底数,减小指数,从而减小乘法的次数,达到快速的目的

接下来就是简书上面的解析啦
快速幂的原理:

a b a^b ab, 我们会发现,其实指数b是可以拆成二进制的。通过式子
a m + n = a m ∗ a n a^{m+n} = {a^m*a^n} am+n=aman,我们可以发现,一旦指数b拆成二进制,那么a^b也可以进行相应的拆分。

例如,当b=11时,b的二进制为1011,即 11 = 1 ∗ 2 0 + 1 ∗ 2 1 + 1 ∗ 2 3 11=1*{2^0}+1*{2^1}+1*{2^3} 11=120+121+123

那么, a 11 = a 1 ∗ 2 0 + 1 ∗ 2 1 + 1 ∗ 2 3 = a 1 ∗ 2 0 ∗ a 1 ∗ 2 1 ∗ a 1 ∗ 2 3 = a 1 ∗ a 2 ∗ a 8 a^{11}=a^{1*{2^0}+1*{2^1}+1*{2^3}}=a^{1*2^0}*a^{1*2^1}*a^{1*2^3}=a^1*a^2*a^8 a11=a120+121+123=a120a121a123=a1a2a8

经过上述操作,要求 a 11 a^{11} a11,我们只需要进行3次计算,而用朴素算法我们需要进行11次计算。

接着就是如何判断一个数在二进制形式的某个位置是0还是1,以及具体的代码实现了。

因为是二进制,我们很容易联想到位运算,这里我们需要用到与运算&和右移运算>>。

&运算:通常用于二进制取位操作,例如一个数x & 1 的结果就是取二进制的最末位的值。
还可以判断这个数的奇偶性,如果x&1==0,则x为偶数;如果x&1==1,则x为奇数。
在二进制中,就只有0和1这两个数,判断奇偶 很方便的判断0和1,
1的话就说明这一位是需要计算的。
个人觉得很巧妙 
>>运算:在这里是作为除法来使用,例如一个数x,x >> 1就表示x右移一位,即x=x/2。

二进制快速幂:

long long Pow2(long long base,long long n,long long MOD){
	/*base为底数,n为指数,MOD是要模的数*/ 
	long long ans=1;
	while(n){//n!=0,n>0
		if(n&1)
			ans = ans * base  %  MOD;
		
		base = base*base  % MOD;
			n >>= 1;
		//  其实就是  n/=2 的意思;用位运算简便一些吧; 
		
	}
	
	return ans;
} 

二进制矩阵快速幂:

/*由于c++ 中没有返回一个数组类型的东东,
所以这个时候就需要来借助结构体来实现返回 矩阵的操作了;*/
struct node {
	int m[N][N];
}; 
long long  MOD ;
node cul(node a,node b){//矩阵的乘法; 
	node ans;
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			ans.m[i][j] = 0;
			for(int k=0;k<N;k++){
				ans.m[i][j] += a.m[i][k]*b.m[k][j]%MOD; 
			}
		}
	}
	return ans;
}
node Pow2(node base,long long n){
	/*base为底数,n为指数,MOD是要模的数*/ 
	node ans;
	
	for(int i=0;i<N;i++){//把ans初始化为单位矩阵E;
		for(int j=0;j<N;j++){
			if(i == j)
				ans.m[i][j]==1;
			else
				ans.m[i][j]=0;
		}
		
	}
	
	while(n){
		if(n&1)
			ans = cul(ans,base);
		//	ans = ans * base  %  MOD;
		
		//base = base*base  % MOD;
		base = cul(base,base);
			n >>= 1;
		//  其实就是  n/=2 的意思;用位运算效率更高; 
		
	}
	
	return ans;
}

接下来
十进制快速幂:
十进制快速幂和二进制没有什么本质的区别,一个是十进制拆分指数,另一个是二进制拆分指数。

例如3
3 405 3^{405} 3405可以拆分为 ( 3 1 ) 5 ∗ ( 3 10 ) 0 ∗ ( 3 100 ) 4 (3^1)^5 * ( 3^{10} )^0 * (3^{100})^4 (31)5(310)0(3100)4.

//十进制快速幂
long long Pow10(long long base,long long MOD){
		long long ans=1,base1;
		while(n){
			int k=n%10;
			base1 = base;
			if(k){
				for(int i=0;i<k;i++)
					base = base*base % MOD;
			}
			for(int i=1;i<=10;i++)
			// 10进制倍增。 当然这个可以用二进制倍增来优化的
				base = base * base1 %MOD;
			
			n/=10;
		}
		return ans;
} 

当指数很大的时候,就是long long(10^19) 都存不下的时候,就需要用字符串来进行存储了;
这个地方的话解决了我之前不理解 就是long long表示不了那个数,但是long long 这个长的字符串就可以存储是什么意思 ,现在弄明白了;
举个简单的例子吧,就10和长度为10的字符串,长度为10 的字符串可以存储10^10大小的数,
也就是说19个长度的字符串就可以存储long long型的数了 。
ps:长度为10的字符串表示的最大10位数,一个表示位数一个表示大小。

long long Pow10(long long base,string  n,long long MOD){
		long long ans=1,base1,len;
		len = n.size()-1;
		while(len){
			int k=(n[len] - '0')%10;
			base1 = base;
			if(k){
				for(int i=0;i<k;i++)
					base = base*base % MOD;
			}
			for(int i=1;i<=10;i++)// 10进制倍增。 
				base = base * base1 %MOD;
			
			len--
		}
		return ans;
} 

十进制矩阵快速幂:
这个其实和十进制快速幂是一样的原理的,只不过是乘的单位从数值变成了矩阵而已。

struct node {
	int m[N][N];
}; 
long long  MOD ;
node cul(node a,node b){//矩阵的乘法; 
	node ans;
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			ans.m[i][j] = 0;
			for(int k=0;k<N;k++){
				ans.m[i][j] += a.m[i][k]*b.m[k][j]%MOD; 
			}
		}
	}
	return ans;
}
long long Pow10(node base,string n){
	/*base为底数,n为指数,MOD是要模的数*/ 
	node ans,tem;
	long long len=n.size()-1;
	for(int i=0;i<N;i++){//把ans初始化为单位矩阵E;
		for(int j=0;j<N;j++){
			if(i == j)
				ans.m[i][j]==1;
			else
				ans.m[i][j]=0;
		}
		
	}
	
	while(len){
			temp = base;
			int k = (n[len]-'0') %10;
				if(k)
				ans = cul(base,ans);
				
			for(int i=0;i<10;i++)
			base = cul(base,temp);	
		len--;
	}
	
	return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值