线形同余方程系列
求解形如 : ax ≡ b (mod n)
方程可以变形为 : ax – ny = b; 对于这个方程的求解可以使用扩展欧几里得定理做,有解的首要条件便是 b 能够整除 gcd(a ,n); 证明如下: 如果b 不能够整除 (a ,n),那么方程两边同除以(a ,n),有 a’x – n’y = b/(a ,n) ; 可以看出,方程左边肯定是整数,右边却为小数,所以b肯定能整除 (a ,n);
对于方程: ax – ny = gcd(a ,n);通过扩展欧几里得可以求出方程的某个解: 假设求得x0,那么方程其余解可以表示为 : x0 + k* n/(a ,n); k∈Z(对于这个通解,我参考很多网上的资料,可是说得都不是很清楚,智商不高的我表示我研究两天才有所领悟) ; 首先,由于y=ax/n; (这里的除法是计算机语言中的除法) 也就是说随着 x 的变化,y是可以相应变化。。 那么,x0满足条件 ,x1=x0+n也满足条件,对应y1=y0+a; 但我们可以缩小这个间距,那么是不是y1=y0+1;满足条件呢?此时的x1=x0+n/a; 但由于求的是整数解,我们无法保证n/a 是一个整数。那么也就是说我们要找的就是t ∈ [1,n],使得n/t是个整数且t最大,当然同时还要满足a*n/t % n==0;
那么就有a/t *n % n ==0; 也就是 a / t 也得是一个整数,那么能够被a、n整除的最大t便是 gcd(a ,n);所以 通解为 xk= x0 + k*n/(a ,n); k∈Z; (为啥总觉得自己把一个很简单的问题想得太复杂呢。。。因为通过这个式子: ,可以很容易看出 通解的间距为: n’; 66666... )
现在来看线性同余方程组:
解决形如: x ≡ b1 (mod a1); ①
x ≡ b2 (mod a2); ②
x ≡ bi (mod ai);
解决方法是两两一对求解,然后再通过得到的解构造一个新的方程,然后递推求解。
首先: 由 ①、② 得 a1 * k1 – a2 * k2 = b2 - b1; ③
那么方程组有解的首要条件便是 gcd(a1,a2) | (b2-b1);如果不满足这个条件,方程无解。
式子 a1 * k1 – a2 * k2 = gcd(a1,a2);通过扩展欧几里得定理可以求出某个解k1, 那么式子 ③ 中的k1=k1*(b2-b1)/(a1,a2);
由于要让k1 尽量小,且为正整数 ; 令 t= a2/(a1,a2); 那么有k1=(k1 % t + t ) % t; (t 的由来参看上部分)
现在可以明了方程组的解为 x = a1 * k1 + b1; ④
那么可以构造出第三个式子 : b1 = x; a1= lcm[a1,a2];
关于构造第三个式子的理解: 如果只有两个方程式子,那么现在得出的解x便是满足条件的最小解x,但如果还有第三或者更多的线性同余方程,那么如果最后的解x’大于x,那么他必然满足 x’% lcm[a1,a2] == x; 即 x’就是 ④ 的通解 。
如果说上面的理解还有需要想象的地方(0.0 我反正找不到...),那么下面我给出比较严谨的公式推导过程:
x ≡ b1 (mod a1); ①
x ≡ b2 (mod a2); ②
由 ① 得 x = a1*k1+b1; ③
将 ③ 代入 ② :有 a1*k1+b1 ≡ b2 (mod a2) ;
--> a1*k1 ≡ b2-b1 (mod a2); ④
令 d= gcd(a1,a2); B= b2-b1;
由 ④ 有: a1*k1 = k2*a2+B;
--> a1*k1/d = k2*a2/d + B/d ;
令B’= B/d;
有 a1*k1/d = k2*a2/d + B’; ⑤
由 ⑤ 得: a1*k1/d ≡ B’(mod a2/d);
--> k1 ≡ K (mod a2/d);
--> k1 = k’*a2/d + K; ⑥
将 ⑥ 代入 ③ :
x = (k’*a2/d + K)*a1+b1;
--> x = k’*a1*a2/d +K*a1 +b1;
--> x ≡ K*a1 +b1 (mod a1*a2/d); ⑦
由 ⑦ 也验证了构造出来的新方程的a’= a1 * a2 / d ; b’= K * a1 + b1 ;
上面讨论的是方程组x前面没有系数的情况,但如果前面存在系数w1,做法也几乎一样,就是先两边同除以gcd(wi,ai);得到式子:wi’* x ≡ bi’(mod ai’),然后化为x ≡ bi’/ wi’(mod ai’);上述过程中如果出现除不尽的情况就表示无解..,
以上的解法是通用于这类型题的,如果题目中多一个限制条件:强调a1、a2、…、ak是互质的,那么可以用中国剩余定理来解,解法如下:
从刚才通用的解法中,我们也可以看出最后的解范围为0<=x<=lcm[a1,a2,…,ak],如果a1、a2等是互质的话,设他们的最小公倍数为M,M=a1*a2*…*ak;记Mi=M/ai;那么必然存在整数解p,q使得Mi*pi+ai*qi=1;(因为gcd(Mi,ai)==1,这个就不用我解释吧),记ei=Mi*pi;那么有:
ei ≡ 0 (mod aj) ; j!=i; 这个比较好理解吧,Mj中包括着ai;
ei ≡ 1 (mod aj) ; j==i; Mj中不包括ai;
所以可以很容易得出,线性同余方程组的通解为 x = e1*b1+e2*b2+ ...+ek*bk ; 这个就是单纯地从定义得到, 比如 :e1 % a1 = 1 ; 那么 e1 * b1 % a1 =b1; 而其他项取余 a1 都等于0,所以得出的这个式子肯定满足上面那k个线性同余方程,当然,如果想要最小正整数解,可以通过加减M来得到。这个适用于a1、a2、…、ak互质的情况。
……以上就是解决线性同余方程组的方法了。
给一道模板题: http://acm.fzu.edu.cn/problem.php?pid=1402
1 #include<stdio.h> 2 typedef long long LL; 3 void exGcd(LL a,LL b,LL &d,LL &x,LL &y) 4 { 5 if(b==0) 6 { 7 d=a; 8 x=1; 9 y=0; 10 } 11 else 12 { 13 exGcd(b,a%b,d,x,y); 14 int t=x; 15 x=y; 16 y=t-a/b*y; 17 } 18 } 19 int main() 20 { 21 LL n,a1,a2,b1,b2; 22 LL x,y,d,ans; 23 while(scanf("%I64d",&n)!=EOF) 24 { 25 ans=1; 26 scanf("%I64d%I64d",&a1,&b1); 27 for(LL i=1;i<n;i++) 28 { 29 scanf("%I64d%I64d",&a2,&b2); 30 exGcd(a1,a2,d,x,y); 31 if((b2-b1)%d!=0) 32 { 33 ans=0; 34 } 35 LL t=a2/d; 36 x=(x*(b2-b1)/d%t+t)%t; 37 b1=a1*x+b1; 38 a1=a1*a2/d; 39 } 40 if(ans!=0) 41 printf("%I64d\n",b1); 42 } 43 return 0; 44 }