快速幂求余算法

快速幂求余算法

为了防止在快速幂乘中导致溢出,我们往往需要对计算结果取余,下面介绍快速幂算法过程以及如何防止溢出。

公式: a b a^b ab m o d mod mod c c c

原始代码:

res = 1;
for(int i=1; i <= b; i++){
	res = res*a;
}
res = res % c;

改进一:

前提: a b a^b ab m o d mod mod c c c = ( a   m o d   c ) b (a \ mod \ c)^b a mod cb m o d mod mod c c c

为了防止 a a a 过大,我们可以按照上述的公式对代码进行如下的改进:

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) ,下面我们对代码的时间复杂度进行改进。我们将 b b b 的情形分为两种,如下:

r e s = { ( a 2 ) b 2 , b 为 偶 数 a ⋅ ( a 2 ) b − 1 2 , 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)2b1bb

则改进的代码如下:

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),但是当 b b b 足够大时,这种写法仍然不是有效的,因此我们可以对上述代码的 for 循环进行改进。

方法就是,上述代码当第一次 for 循环时计算的是 a 2 a^2 a2 ,第二次 for 循环时计算的是 a 4 a^4 a4,之后为 a 6 a^6 a6 a 8 a^8 a8 a 2 k a^{2k} a2k…… (k=1,2…… b/2 )

我们可以使用迭代的方式来替换,如 a 2 a^2 a2 a 4 a^4 a4 a 8 a^8 a8 a 2 n a^{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;

通过以上的改进,我们的时间复杂度变为了 log ⁡ 2 b \log_2b log2b,以上就是快速幂求余算法。

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值