快速幂求余算法
为了防止在快速幂乘中导致溢出,我们往往需要对计算结果取余,下面介绍快速幂算法过程以及如何防止溢出。
公式:aba^bab modmodmod ccc
原始代码:
res = 1;
for(int i=1; i <= b; i++){
res = res*a;
}
res = res % c;
改进一:
前提: aba^bab modmodmod ccc = (a mod c)b(a \ mod \ c)^b(a mod c)b modmodmod ccc
为了防止 aaa 过大,我们可以按照上述的公式对代码进行如下的改进:
res = 1;
a = a % c //对a取余
for(int i=1; i <= b; i++){
res = res*a;
}
res = res % c;
改进二:
由于某个因子取余之后相乘再取余保持余数不变,那么新算得的res也可以进行取余,这样就避免了在计算过程中出现溢出的情况。
res = 1;
a = a % c //对a取余
for(int i=1; i <= b; i++){
res = (res * a) % c; //对每一次的计算结果进行取余
}
res = res % c;
改进三:
到现在为止上述代码的时间复杂度仍然为 O(b)O(b)O(b) ,下面我们对代码的时间复杂度进行改进。我们将 bbb 的情形分为两种,如下:
res={(a2)b2,b为偶数a⋅(a2)b−12,b为奇数res = \begin{cases} (a^2)^\frac {b}{2} & ,b 为偶数\\ a \cdot (a^2)^\frac {b-1}2 & ,b为奇数 \end{cases}res={(a2)2ba⋅(a2)2b−1,b为偶数,b为奇数
则改进的代码如下:
res = 1;
a = a % c //对a取余
k = (a*a) % c; //令 K = a^2
if(b%2 == 1){
res = (res * a) mod c; //如果是奇数,要多求一步,可以提前算到res中
}
for(int i=1; i <= b/2; i++){
res = (res * k) % c; //对每一次的计算结果进行取余
}
res = res % c;
改进四:
我们虽然对该算法的时间复杂度做了改进,现在的时间复杂度为 O(b/2)O(b/2)O(b/2),但是当 bbb 足够大时,这种写法仍然不是有效的,因此我们可以对上述代码的 for 循环进行改进。
方法就是,上述代码当第一次 for 循环时计算的是 a2a^2a2 ,第二次 for 循环时计算的是 a4a^4a4,之后为 a6a^6a6、a8a^8a8 、a2ka^{2k}a2k…… (k=1,2…… b/2 )
我们可以使用迭代的方式来替换,如a2a^2a2 、a4a^4a4、 a8a^8a8、a2na^{2^n}a2n……
改进代码如下:
res = 1;
a = a % c;
while(b > 0){
if (b%2 ==1 ) res = (res*a) mod c;
a = (a*a) mod c;
b = b/2;
}
return res;
通过以上的改进,我们的时间复杂度变为了 log2b\log_2blog2b,以上就是快速幂求余算法。

2180

被折叠的 条评论
为什么被折叠?



