废话不多说, 直接步入正题。
现在oj网站的题或者竞赛的题,如果a的b次幂且b很大,那么题中大多会让你把结果对一个数取余也就是求模,例如a^b%c这种,当然如果是考高精度的题除外。
接下来我将提供一种常规算法和两种竞赛中主流的快幂算法。
首先我们设题目要求为a^b mod c
常规算法
这里我就不多作解释,直接码代码了
int mod(int a, int b, int c){
int i, ans = 1;
for (i = 0; i < b; i++)
ans = (ans * a) % c; //在这里对c取余是为了防止数据溢出
return ans;
}
这个算法的时间复杂度为O(n),不是很高,但是一旦题目中b的取值很大且时间要求高的话,那么这个时间复杂度就危险了,接下来得靠我们的快幂算法来摆平。
快幂算法1
这里我们需要两个公式:
这两个公式都不难理解,自己可以验证一下,3^4 = 9^2。
有了这两个公式之后我们就可以考虑思路了。
我们就以b为偶数来举例。
a^b%c = ((a^2)^b/2)%c;
在这里我们假设b/2还是偶数,那么
((a^2)^b/2)%c = (((a^2)^2)^(b/2)/2)%c;到这里就可以了.
我相信稍微懂点算法的人都可以看出来这是一个递归的过程,如果不理解那么自己再找几个数试一试
理解之后就可以看这个快幂的代码了
int quickMod(int a, int b, int c)
{
int ans = 1;
while (b)
{
if (b % 2 == 1)
ans = (ans * a) % c;
b /= 2;
a = (a * a) % c;
}
return ans;
}
这个算法的时间复杂度为O(logn),对于大多数oj网站或者竞赛题相信都可以AC过去。
接下来这种算法虽然思路和上面的完全不一样, 但是其思想核心和上面的算法是有着异曲同工之妙啊。
快幂算法2
我们先将b按2进制展开假设b = 10, 那么b的二进制为1010,也就是0*2^0+1*2^1+0*2^2+1*2^3 = 10;
所以 a^b = a^(0*2^0+1*2^1+0*2^2+1*2^3 ) = a^(2^1) * a(2^3);这种简单的转换在初中就学过了吧,相信大家都懂
所以a^b%c = a^(2^1) * a(2^3) % c =( a^(2^1) % c) * (a(2^3)%c)%c;
讲到这里就可以写代码了
int quickMod(int a, int b, int n)
{
int ans = 1;
while (b)
{
if (b & 0x1) //如果二进制位为1
ans = ans * a % n;
a = a * a % n;
b >>= 1; //b向又移1位
}
return ans;
}
写到这里就差不多该结束了,相信有心的读者可以发现这两种快幂思想的核心都是二分法,所以时间复杂度都为O(logn)。
虽然推倒过程完全不一样,但是到代码实现这个层面几乎是一模一样的,if(b % 2 == 1) 就相当于if(b & 0x1), b /= 2也就是b>>=1。
所以算法是多么优美又多么奇妙,希望大家可以在算法之路上不断前行,继续努力!
也欢迎关注我的公众号,从自身和身边经历聊一些职场话题,带你避坑