快速幂及快速幂取余

快速幂及快速幂取余


快速幂
当遇到求 a n a^n an这种问题时,可以将指数n拆开于是变成 ( a 2 ) n / 2 (a^2)^{n/2} (a2)n/2。然后重复将n除2,底数平方,直到指数为0为止就是结果。于是解决这题的时间复杂度从 O ( n ) O(n) O(n)(普通循环相乘a的做法)到了 O ( l o g n ) O(logn) O(logn)
这就是快速幂算法,本身还是挺简单的,主要思想就是让底数变大,指数不断缩小。
唯一要考虑的就是当n是奇数时需要将式子变成 a ∗ a n − 1 a*a^{n-1} aan1,然后再n除二,底数平方(因为小数次方计算机不好计算),代码如下。

int f(int a,int n)
{
	int ans=1;
	while(n){
		if(n%2){			//一个技巧是把n%2改成n&1也能达到一样的效果,而位运算的效率要高于求余
			n-=1;			//n是奇数时
			ans*=a;			//最后n=1的时候会从这里结束
		}else{
			n/=2;			//n是偶数时		也有一个技巧是n/=2改成n>>=1
			a*=a;
		}
	}
	return ans;
}

可是滑稽的是因为指数爆炸,a=2 n等于64时的结果便超过了长整形的数据范围,所以这个算法并没有卵用,所以这个模板只适用于大数模拟的快速幂,或者将这个算法与快速幂求余一起讨论。

快速幂取余
当遇到求 a n % b a^n\%b an%b这种问题时,可以利用求余的基本运算法则,基本运算法则如下:
   1. ( a + b ) % c = ( a % c + b % c ) % c 1.(a+b)\%c=(a\%c+b\%c)\%c 1.(a+b)%c=(a%c+b%c)%c
   2. ( a − b ) % c = ( a % c − b % c ) % c 2.(a-b)\%c=(a\%c-b\%c)\%c 2.(ab)%c=(a%cb%c)%c
   3. ( a ∗ b ) % c = ( a % c ∗ b % c ) % c 3.(a*b)\%c=(a\%c*b\%c)\%c 3.(ab)%c=(a%cb%c)%c
类似于普通四则运算,不过没有除法。
第三点可以容易证明 a b % c = ( a % c ) b % c a^b\%c=(a\%c)^b\%c ab%c=(a%c)b%c

利用这点和快速幂算法就可把此问题中的指数除二得到 ( ( a 2 ) n / 2 ) % b = ( ( a 2 % b ) n / 2 ) % b ((a^2)^{n/2})\%b=((a^2\%b)^{n/2})\%b ((a2)n/2)%b=((a2%b)n/2)%b  (n为偶数),接下来就是和快速幂的做法一样了,将n不断缩小直至为1为止,同样也要考虑奇数,所以代码只需稍作修改如下。

int f(int a,int n,int b)
{
	int ans=1;
	a%=b;					//防止a*a溢出
	while(n){
		if(n&1){
			n-=1;			
			ans=(ans*a)%b;	//只需要加个求余符号即可
		}else{
			n>>=1;			
			a=(a*a)%b;
		}
	}
	return ans%b;			//多求余一次防止n等于0时出现意外错误
}

以上便是快速幂取模,复杂度同样是 O ( l o g n ) O(logn) O(logn)。要注意的是 b 2 b^2 b2不能超出数据范围,不然会溢出。
快速幂取模可以常见使用于求某一个 a n a^n an的后几位数,比如 a n % 1000 a^n\%1000 an%1000就是得到后三位数的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值