快速幂算法

前言

快速幂算法


求a^b%c的值

其中a,b,c是整数,且0<a,c<1e9,0<b<1e18

分析

我们首先能考虑到的就是用循环直接计算a^b的值,然后再对c取模。代码如下

long long ans=1;
for(long long i=1;i<=b;i++)
{
    ans*=a;
}
ans%=c;

但是这个代码有很大的缺陷,比如复杂度是O(b)而b的值是1e18,并且ans很有可能超过long long 所以该代码需要优化。
我们根据取模运算符的性质:(ab)%p=[(a%p)(b%p)]%p
证明:
设a=k1p+q1 b=k2p+q2
(ab)=(…)p+q1+q2
(a
b)%p=(q1*q2)%p
a%p=q1 b%p=q2
现在我们可以每乘一次对p取模,但是现在仍然需要循环b次,那还有没有优化的方法呢?
当我们考虑手算3^10时我们可以怎样方便的计算呢
在这里插入图片描述
我们发现每次指数b都除2那么此时时间复杂度就降为O(log2b)

代码如下:

int fast_power(int a,int b,int c)
{
	int ans=1;
	a%=c;
	while(b)
	{
		if(b%2==1)
		{
			ans=(ans*a)%c;
		}
		a=(a*a)%c; 
		b/=2;
	}
	return ans;
}

代码优化

int fast_power(int a,int b,int c)
{
	int ans=1;
	a%=c;
	while(b)
	{
		if(b&1)
		{
			ans=(ans*a)%c;
		}
		a=(a*a)%c; 
		b>>=1;
	}
	return ans;
}

我们使用位运算符可以更加简化这个代码

b&1:

将 b 转换为二进制表示: 你需要先把 b 转换为二进制数。例如,如果 b 是一个整数 5,它的二进制表示是 101。

将 1 转换为二进制表示: 1 的二进制表示是 1。如果 b 是一个多位数,那么 1 应该用适当数量的零填充在前面以匹配 b 的位数。例如,如果 b 是 8 位的 00000101,那么 1 应该是 00000001。

对齐这两个二进制数: 如果 b 是多位的,将 1 填充到与 b 相同的位数。

执行位与运算: 对应位执行与运算。如果两位都是 1,结果是 1;否则,结果是 0。

例如:
b = 00000101 (二进制)
1 = 00000001 (二进制)
b & 1 = 00000001 (二进制)
将结果转换回十进制: 结果 00000001 转换回十进制就是 1。
所以,b & 1 计算的是 b 的二进制表示的最低位(最右边的一位)的值。具体来说,b & 1 的结果是 b 是否是奇数的指示:如果 b 是奇数,结果是 1;如果 b 是偶数,结果是 0。

b>>=1:

将 b 转换为二进制表示: 例如,如果 b 是 13,它的二进制表示是 1101。
执行右移运算: 将二进制数向右移动一个位置。右移时,最右边的位将被丢弃,左边会根据符号位填充。
对于无符号整数,左边的位填充为 0。
对于有符号整数(例如,32 位带符号整数),右移时左边的位根据符号位(即最高位)来填充。对于正数,填充 0;对于负数,通常使用算术右移(填充符号位)。
例如,13 的二进制表示是 00001101。右移一位后变成 00000110。
计算结果: 移动后的结果就是 b 除以 2 的整数部分。对于 13,右移一位得到的结果是 6,因为 13 / 2 = 6。
具体例子:
b = 13 的二进制是 00001101。
b >>= 1 后变成 00000110,即十进制的 6。
对于负数,例如 b = -5(假设使用 8 位二进制表示,补码表示),其二进制表示是 11111111(因为 -5 的补码是 11111111)。右移一位后变成 11111111(因为是负数,使用算术右移,符号位填充 1)。
计算结果为 -3,因为 -5 除以 2 的结果是 -3。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值