快速幂是一种对幂取模的快速算法,可以用a^b mod c来表示。
从最原始的开始分析,我们先用c来实现下这个函数:
int powerMod(int a, int b, int c) {
int power = 1;
for(int i=0;i<b;i++) {
power *= a;
}
return power%c;
}
OK,这个代码不解释,分析下,时间复杂度为O(b),当然很明显这个时间复杂度并不是最大的问题,最大的问题在与当a和b的值比较大时(特别是b),power很有可能溢出。
其实很容易证明,a^b mod c = (a mod c)^b mod c,先乘再取模和先取模再乘其实是一样的,那么我们可以做出如下优化:
int powerMod(int a, int b, int c) {
int power = 1;
a = a%c;
for(int i=0;i<b;i++) {
power = (power*a)%c;
}
return power%c;
}
首先a先对c取模,这样可以保证每次乘上去的a都不会太大,然后可以每次都让乘积对c取模(先乘再取模和先取模再乘结果是一样的),这样的话,power溢出的问题已经解决。
当然,既然是叫快速幂,那么对于目前的时间复杂度O(b)还可以再进行优化。现在的瓶颈在与求a的b次方,循环了b次。根据a^b = (a^2)^(b/2),如果a和b都是整数,那么需要对b的奇偶性分开讨论:
b为偶数时,a^b = (a^2)^(b/2)
b为奇数时,a^b = a*(a^2)^(b/2)
这样,我们成功把O(b)降到了O(b/2),很明显这是可以递归推理的,那么最终可以降到O(log(b))。
优化后的代码:
int powerMod(int a, int b, int c) {
int power = 1;
a %= c;
while(b>0) {
if(b%2) {
power = (power*a)%c;
}
b /= 2;
a = (a*a)%c;
}
return power%c;
}
最终,我们优化到的时间复杂度为O(log(b)),其实上面的有些地方(比如先乘再取模和先取模再乘结果是一样的)是需要数学公式证明的,后面有时间再补上。