快速幂(C/C++)

目录

引入

朴素解法

接下来,问题更进一步:

快速幂:

快速幂的递归写法:

快速幂的迭代写法


引入

给定三个正整数a, b, m (a < 10^9, b < 10^6, 1 < m < 10^9), 求 a^b % m。

注:^表示乘方运算符

你可能觉得,咱只要学过循环不就会写(有手就行)

没错,这里给一个时间复杂度为O(b)的解法

朴素解法

typedef long long LL;
LL fn(LL a, LL b, LL m){
	LL ans = 1;
	for(int i = 0; i < b; ++i){
		ans = ans * a % m;
	}
	return ans;
}

接下来,问题更进一步:

给定三个正整数a, b, m (a < 10^9, b < 10^18, 1 < m < 10^9), 求 a^b % m。

 对于O(b)的复杂度,支持b < 10^8都已经困难,罔论10^18

那怎么办呢?

快速幂:

由于它基于二分的思想,也被称为二分幂

快速幂基于以下事实:

1. 如果b是奇数,a^b = a * a^b-1;

2. 如果b是偶数, a^b = a^(b/2) * a^(b/2);

因此,对于一个b,我们基于上述事实可以把问题的范围从a^b一直缩小到a^0。

没错,就是递归

快速幂的递归写法:

typedef long long LL;
LL binaryPow(LL a, LL b, LL m){
	if(b == 0) return 1;
	if(b % 2 == 1) return a * binaryPow(a, b-1, m) % m;
	else{
		LL tmp = binaryPow(a, b/2, m);
		return tmp * tmp % m;
	}
}

请您注意两点:

1. 初始的时候,a可能大于m,那需要在进入函数前先对a取模;

2.如果m = 1,可以直接结果特判为0,因为任何正整数 % 1均为0;

快速幂的迭代写法

对a^b来说,b可以写成若干二进制之和,如13 = 2^3 + 2^2 +2^0 = 8 + 4 + 1,所以2^13 = 2^8 + 2^4 + 2^1。

很容易得知,我们其实可以把任意a^b表示成a^2k, ..., a^1的乘积

如果b的二进制的i号位为1,那么a^2^i这项就会被选中。

因此,我们得到了计算a^b的大致思路:令i从0到k枚举b的二进制的每一位,如果当前位为1,那么我们就把a^2^i这项捡起来。

不难注意到,a^2k, ..., a^4, a^2, a^1的前一项总是等于后一项的平方,

所以可以用如下策略来具体实现:

1. 初始的时候令ans = 1, 用来存放累积的结果;

2. 判断b的二进制末尾是否为1(即判断b & 1是否为1, 也可以理解为判断是否为奇数),如果是的话,令ans乘上a的值;

3. 令a平方,并将b右移一位(也可以理解为b/2)

4. 只要b大于0,就返回第2步。

代码如下:

typedef long long LL;
LL binaryPow(LL a, LL b, LL m){
	LL ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % m;
		}
		a = a * a % m;
		b >>= 1;
	}
	return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AryCra_07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值