当要求a ^ b % p时,若a、b、p的数据不算很大时直接调用pow()函数最为方便,但考虑a、b、p的数量级均达到千万甚至上亿时想用pow()在短时间内得到结果几乎是不可能的想法甚至计算机还可能出现爆long的情况,这时候快速幂相当排得上用场。
我们都知道取模运算中 。
幂运算a ^ b本质也是一种乘法运算即b 个 a 相乘,所以由上式可得在每次“×”的时候先取一次模在相乘,循环下去最终可以得到结果,快速幂借用这种思想一次一次地把幂降下来,当幂为1时,底数即为结果,降幂均为折半(若为奇数则减一折半)。
为了保持结果不变,幂的折半同时对应底数的翻倍,并且取一次模运算,对比传统求幂的做法要循环n次(n为幂),而快速幂只需循环不超过logn次运算即可得到最终结果,这也正是其优化后时间效率较高的地方。
朴素做法:
long long quick_pow(long long base, long long power, long long p){
long long res = 1; //迭代的结果
while(power > 0){
if(power % 2 == 0){
power /= 2;
base = base * base % p;
}
else{
power -= 1;
res = res * base % p; /*幂为奇数减一位后,底要多乘一次,而这次记录到res上代替base ^ power的结果*/
power /= 2;
base = base * base % p;
}
}
return res % p;
}
再快一点:
上面的做法已经实现了快速幂算法,而还可以对其再进行优化一下使代码更简洁,我们都知道机器处理位运算时非常快,所以考虑将判断奇偶运算用"&"来代替,即power & 1 == 0表示为偶数;同时还能将折半运算用">>"来代替,即power >>= 1,右移一位。而奇偶情况下都有power /= 2; base = base * base % p;操作,不妨将其拿出判断语句,只是在power为奇时把要乘的一次base记录到res上。所以最终就可以简化为如下形式:
long long quick_pow(long long base, long long power, long long p){
long long res = 1; //迭代的结果
while(power){
if(power & 1 == 1){
res = res * base % p;
}
power >>= 1;
base = base * base % p;
}
return res % p;
}