大数越界
当a增大时,最后韩慧的 3 a 3^a 3a大小以指数级增长,可能超出int32甚至int64的取值范围,导致返回值错误。
大数求余问题:在仅使用int32类型存储的前提下,正确计算 x a x^a xa对p求余(即 x a x^a % p xa ⊙ p)问题。
解决方案:循环求余,快速幂求余,其中后者的时间复杂度更低,两种方法均基于以下求余运算规则推出
( x y ) ⊙ p = [ ( x ⊙ p ) ( y ⊙ p ) ] ⊙ p (xy)⊙p=[(x⊙p)(y⊙p)]⊙p (xy)⊙p=[(x⊙p)(y⊙p)]⊙p
1.循环求余:
- 根据求余运算性质推出(∵本题中 x < p x<p x<p,∴ x ⊙ p = x x⊙p=x x⊙p=x ):
x a ⊙ p = [ ( x a − 1 ⊙ p ) ( x ⊙ p ) ] ⊙ p = [ ( x a − 1 ⊙ p ) x ] ⊙ p x^a⊙p=[(x^{a-1}⊙p)(x⊙p)]⊙p=[(x^{a-1}⊙p)x]⊙p xa⊙p=[(xa−1⊙p)(x⊙p)]⊙p=[(xa−1⊙p)x]⊙p
- 解析:利用此公式,可通过循环操作依次求 x 1 , x 2 , x 3 , . . . x a − 1 x^1,x^2,x^3,...x^{a-1} x1,x2,x3,...xa−1对p的余数,保证每轮中间值都在int32取值范围内,封装方法代码如下所示。
- 时间复杂度 O ( N ) O(N) O(N):其中N=a,即循环的线性复杂度。
// (x^a) % p —— 循环求余法
int remainder(int x,int a,int p):
{
int rem = 1;
for(int i=0:i < a;i++){
rem = (rem * x) % p;
}
return rem;
}
2、快速幂求余:
- 根据求余运算性质可推出:
- x a ⊙ p = ( x 2 ) a / 2 ⊙ p = ( x 2 ⊙ p ) a / 2 ⊙ p x^a⊙p=(x^2)^{a/2}⊙p=(x^2⊙p)^{a/2}⊙p xa⊙p=(x2)a/2⊙p=(x2⊙p)a/2⊙p
- 当a为奇数时a/2不是整数,因此分为以下两种情况,("//"代表向下取整的除法):
x a ⊙ p = { ( x 2 ⊙ ) a / / 2 ⊙ p , a为偶数 [ ( x ⊙ p ) ( x a − 1 ⊙ p ) ] ⊙ p = [ x ( x 2 ⊙ p ) a / / 2 ] ⊙ p , a为奇数 x^a⊙p= \begin{cases} (x^2⊙)^{a//2}⊙p,& \text{a为偶数}\\[5ex] [(x⊙p)(x^{a-1}⊙p)]⊙p=[x(x^2⊙p)^{a//2}]⊙p, &\text{a为奇数} \end{cases} xa⊙p=⎩⎪⎪⎨⎪⎪⎧(x2⊙)a//2⊙p,[(x⊙p)(xa−1⊙p)]⊙p=[x(x2⊙p)a//2]⊙p,a为偶数a为奇数 - 解析 :利用以上公式,可通过循环操作每次吧指数a问题降至指数a//2问题,只需循环 l o g 2 ( N ) log2(N) log2(N)次,因此可将复杂度降至对数级别。封装方法代码如下所示:
int reminder(int x,int a,int p)
{
int rem = 1;
while(a > 0){
if (a % 0){
rem = (rem * x) % p;
}
x = x ^ 2 % p;
a //=2;
}
return rem;
}
- 帮助理解:根据下表,初始状态 r e m = 1 , x = 3 , a = 19 , p = 1000000007 , rem=1,x=3,a=19,p=1000000007, rem=1,x=3,a=19,p=1000000007,最后会将 r e m ∗ ( x a ⊙ p ) rem*(x^a⊙p) rem∗(xa⊙p)化为 r e m ∗ ( x a ⊙ p ) = r e m ∗ 1 rem*(x^a⊙p)=rem*1 rem∗(xa⊙p)=rem∗1的形式,即 r e m rem rem为余数答案。
-