大数取余解法

大数越界

当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=[(xp)(yp)]p

1.循环求余:

  • 根据求余运算性质推出(∵本题中 x < p x<p x<p,∴ x ⊙ p = x x⊙p=x xp=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 xap=[(xa1p)(xp)]p=[(xa1p)x]p

  • 解析:利用此公式,可通过循环操作依次求 x 1 , x 2 , x 3 , . . . x a − 1 x^1,x^2,x^3,...x^{a-1} x1,x2,x3,...xa1对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 xap=(x2)a/2p=(x2p)a/2p
  • 当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} xap=(x2)a//2p,[(xp)(xa1p)]p=[x(x2p)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) remxap化为 r e m ∗ ( x a ⊙ p ) = r e m ∗ 1 rem*(x^a⊙p)=rem*1 rem(xap)=rem1的形式,即 r e m rem rem为余数答案。
    -在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43679351/article/details/124902841