写在前面:
记录了个人的学习过程,同时方便复习
笔者渣渣,网上的证明看不懂,只能自己证
注意不要把中国剩余定理和解线性同余方程组混为一谈
中国剩余定理仅仅是一个很巧妙的算法
在学习如何解线性同余方程组之前,根本没必要学习中国剩余定理
本篇全原创,转载请留言
目录 |
by otakuap
同余方程组的解什么时候存在呢?
先假设要解这么一个同余方程组:
x ≡ a1 (mod b1)
x ≡ a2 (mod b2)
……
x ≡ an (mod bn)
我们假设其中的每一个同余方程都成立!
我在[◹]拓展欧几里得算法的应用④中提到了同余方程的一个有趣的性质:放缩性
利用放缩,我们来把所有的模数变成一样的!
定义B=Πni=1 bi
根据放缩,则上面那个同余方程组可以转化为:
x*B/b1 ≡ a1*B/b1 (mod B)
x*B/b2 ≡ a2*B/b2 (mod B)
……
x*B/bn ≡ an*B/bn (mod B)
在同余方程组中有这么一个显然的性质:
在同一模数的意义下,同一个数的对应值不能有多个!(在函数中可以多对一,不能一对多)
一旦出现
x*B/bi ≡ c (mod B)
x*B/bi ≡ d (mod B)
c,d为非负整数,且c!=d
那么一定无解
这就是同余方程组有解无解的本质了
在这里可以得到上面的线性同余方程组的成立条件:
① 每一组同余方程都有解
(详见[◹]拓展欧几里得算法应用④)
② 每一个x*B/bi都唯一对应一个ai*B/bi
只要以上条件有一个不满足,那么这组同余方程一定无解!
在中国剩余定理解线性同余方程组中,模数两两互质
保证了对应的唯一性
这就是为什么中国国剩余定理来解线性同余方程组,要么不能解,要么一定有解
首先假设有多个周期函数f(x),他们的周期分别为Ti
这多个周期函数方程组成的方程组的解的最小正周期为T
则显然,T为Ti的最小公倍数
同理
先假设要解这么一个同余方程组:
x ≡ a1 (mod b1)
x ≡ a2 (mod b2)
……
x ≡ an (mod bn)
我们假设其中的每一个同余方程都成立!
利用放缩,我们来把所有的模数变成一样的!
定义B=LCM(bi)
注意此处是最小公倍数而不单单是积
根据放缩,则上面那个同余方程组可以转化为:
x*B/b1 ≡ a1*B/b1 (mod B)
x*B/b2 ≡ a2*B/b2 (mod B)
……
x*B/bn ≡ an*B/bn (mod B)
解这么一组线性同余方程组,如果有解,那么它的最小非负整数解一定是[0,B-1]这个区间中的某个整数!
某只马血的教训
先来看一个同余方程组中有趣的性质:
性质: A1 ≡ B1 (mod C) A2 ≡ B2 (mod C) 如果这两个同余式都成立,那么一定有: A1+A2 ≡ (B1+B2)%C (mod C) 暂且叫这条性质为可加性
证明: 根据%运算的分配律 (a+b)%c == ((a%c)+(b%c))%C 则有 若a ≡ a%c (mod c) b ≡ b%c (mod c) 则a+b ≡ ((a%c)+(b%c))%c (mod c) |
这个尝试的思路,大概是先把模数都弄成一样的,然后转化出该线性同余方程组的最终同余方程
利用[◹]拓展欧几里得算法的应用④,判定这个最终方程有没有解,解出来是多少
如果有解,不能说明该线性同余方程组有解
如果无解,不能说明该线性同余方程组无解
为什么呢?
因为可加性的逆命题是假命题
举个例子:
x ≡ 2 (mod 3)
x ≡ 5 (mod 6)
x ≡ 2 (mod 7)
根据放缩,有
14x ≡ 28 (mod 42)
7x ≡ 35 (mod 42)
6x ≡ 12 (mod 42)
根据可加性,有
27x ≡ 33 (mod 42)
解得一个x==9
反带回去:
14*9 ≡ 0 (mod 42)
7*9 ≡ 21 (mod 42)
6*9 ≡ 12 (mod 42)
根本不成立
同余方程组中还有一个趣的性质:
性质: A1 ≡ B1 (mod C) A2 ≡ B2 (mod C) 如果这两个同余式都成立,那么一定有: A1*A2 ≡ (B1*B2)%C (mod C) 暂且叫这条性质为可乘性
证明: 根据%运算的分配律 (a*b)%c == ((a%c)*(b%c))%C 则有 若a ≡ a%c (mod c) b ≡ b%c (mod c) 则a*b ≡ ((a%c)*(b%c))%c (mod c) |
和可加性一样,利用可乘性求解x的尝试也是失败的
因为可乘性的逆命题是假命题
举个例子:
x ≡ 5 (mod 6)
x ≡ 2 (mod 7)
根据放缩,有
7x ≡ 35 (mod 42)
6x ≡ 12 (mod 42)
根据可乘性,有
42x ≡ 0 (mod 42)
显然,x为任意整数
更别说什么反带回去了
虽然这两个尝试都失败了,但是它们还是很有价值的
首先,在[◹]拓展欧几里得算法中提到的放缩的逆命题是真命题
其次,我们已经知道了什么情况下,待解的线性同余方程组有解
我们可以采用拆分,合并的解法来解决问题:
先假设要解这么一个同余方程组:
x ≡ a1 (mod b1)
x ≡ a2 (mod b2)
……
x ≡ an (mod bn)
其中可能有些同余方程根本就没有解,详见[◹]拓展欧几里得算法
定义B=LCM(bi)
根据放缩,则上面那个同余方程组可以转化为:
x*B/b1 ≡ a1*B/b1 (mod B)
x*B/b2 ≡ a2*B/b2 (mod B)
……
x*B/bn ≡ an*B/bn (mod B)
根据有无解的条件②,先判断有没有一对多的情况
如果有,那么肯定无解
如果没有,那么可能有解,继续
然后根据有无解的条件①,一个个判断有无 无解的同余方程 并求出xi,xi都是最小非负整数解
如果存在无解的同余方程,那么一定无解
如果不存在无解的同余方程,那么一定有解
待完成
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int const MAXN=10010; 6 int x,y,tmpb,tmpa,tmp,times; 7 int k;//k组同余方程 8 int a[MAXN];//每个同余方程的余数 9 int b[MAXN];//每个同余方程的模数 10 int bi[MAXN],ai[MAXN]; 11 int B=1; 12 13 void C_swap(int &a,int &b){int c=a;a=b;b=c;return;} 14 15 int exgcd(int a,int b) 16 { 17 if(!b){ 18 x=1;y=0; 19 return a; 20 } 21 int ret=exgcd(b,a%b); 22 int tmp=x; 23 x=y;y=tmp-(a/b)*y; 24 return ret; 25 } 26 27 int lcm(int a,int b){ 28 return a/exgcd(a,b)*b; 29 } 30 31 void ponySort(int l,int r){ 32 int i=l,j=r,mid=bi[(l+r)>>1]; 33 while(i<=j){ 34 while(bi[i]<mid) ++i; 35 while(bi[j]>mid) --j; 36 if(i<=j){ 37 C_swap(bi[i],bi[j]); 38 C_swap(ai[i],ai[j]); 39 ++i;--j; 40 } 41 } 42 if(l<j) ponySort(l,j); 43 if(i<r) ponySort(i,r); 44 return; 45 } 46 47 int main(int argc,char *argv[],char *enc[]) 48 { 49 scanf("%d",&k); 50 for(int i=1;i<=k;++i) 51 scanf("%d%d",&b[i],&a[i]); 52 53 for(int i=1;i<=k;++i) 54 printf("x ≡ %d (mod %d) ",a[i],b[i]); 55 printf(" "); 56 57 for(int i=1;i<=k;++i) 58 B=lcm(B,b[i]); 59 60 for(int i=1;i<=k;++i){ 61 bi[i]=B/b[i]; 62 ai[i]=B/b[i]*a[i]; 63 } 64 65 for(int i=1;i<=k;++i) 66 printf("x*%d ≡ %d (mod %d) ",bi[i],ai[i],B); 67 printf(" "); 68 69 ponySort(1,k); 70 71 for(int i=1;i<=k;++i) 72 printf("x*%d ≡ %d (mod %d) ",bi[i],ai[i],B); 73 printf(" "); 74 75 for(int i=1;i<=k;++i) 76 { 77 if(tmpa!=ai[i] && tmpb==bi[i]){ 78 printf("NOPE "); 79 return 0; 80 } 81 tmpa=ai[i]; 82 tmpb=bi[i]; 83 } 84 85 /* xi*bi[i] ≡ ai[i] (mod B) */ 86 87 for(int i=1;i<=k;++i){ 88 89 tmp=exgcd(bi[i],B); 90 91 if(ai[i]%tmp!=0){ 92 printf("NOPE "); 93 return 0; 94 } 95 else{ 96 times=0; 97 while(ai[i]%tmp==0){ 98 ai[i]/=tmp; 99 ++times; 100 } 101 102 if(tmp!=B){ 103 x*=ai[i]; 104 x=((x%(B/(tmp*times)))+(B/(tmp*times)))%(B/(tmp*times)); 105 printf("%d*%d ≡ %d (mod %d) ",x,bi[i],ai[i]*times*tmp,B); 106 } 107 else{ 108 printf("NOPE "); 109 return 0; 110 } 111 } 112 } 113 114 return 0; 115 } 116 /* 117 3 118 3 2 119 6 5 120 7 2 121 */