模线性方程组(转载)
/**********************欧几里得算法***********************/
其实很早就知道这个东西了。。也就是以前所说的辗转相除法求最大公约数。GCD(a,b)=GCD(b,a mod b)
稍微证明一下:(参考:算法导论)
证明的思路是大致是这样的:证明 GCD(a,b) | GCD(b,a mod b) 并且 GCD(b,a mod b) | GCD(a,b)
先证GCD(a,b) | GCD(b,a mod b):
设 d=GCD(a,b),则
d|a 并且 d|b
那么 a=r+kb (k为整数)
也就是说 (a mod b) 是a和b的线性组合,所以有
d|(a mod b)
因为 d|b 并且 d|(a mod b)
所以 d|GCD(b,a mod b)
也就是 GCD(a,b) | GCD(b,a mod b)
类似的,GCD(b,a mod b) | GCD(a,b)
大家都会证这个了吧?真聪明。。。。。。。。。
代码我试了三个,其中第三个是算法导论那章末尾的习题。看着觉得很NB,不过稍微测试了下(10^5个int,10^6个int,10^6个long long)。。发现效率也优不了多少,时间都差不多。。也许是我没写好吧。。
建议一般的可以直接用递归的版本(第一种)。非递归版本(第二种)是以防万一。至于位运算的那个版本(第三种)。。。。以后再查查看怎么写才有效率吧。。
//Least Common Multiple
顺便一提,LCM(a,b)=a*b/GCD(a,b)。这在求解模线性方程组时有一定的用处。
这个可以用唯一分解定理证。
a和b都可唯一分解成以下形式:
a=p1^s1*p2^s2*...*pn^sn
b=p1^t1*p2^t2*...*pn^tn
其中,pi为质数,si和ti为非负整数。那么
GCD(a,b)=p1^min(s1,t1)*p2^min(s2,t2)*...*pn^min(sn,tn)
LCM(a,b)=p1^MAX(s1,t1)*p2^max(s2,t2)*...*pn^max(sn,tn)
显然有
GCD(a,b)*LCM(a,b)=a*b
所以,LCM(a,b)=a*b/GCD(a,b)
证毕..
1 //Greatest Common Divisor 2 //递归 3 long long GCD(const long long &a,const long long &b) { 4 return b?GCD(b,a%b):a; 5 } 6 7 //非递归~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 long long GCD(long long a,long long b) { 9 while(b) { 10 long long t=a%b; 11 a=b; 12 b=t; 13 } 14 return a; 15 } 16 17 //位运算~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 long long GCD(long long a,long long b) { 19 long long d=1; 20 while (a&&b) 21 if (~a&1) 22 if (~b&1) d<<=1,a>>=1,b>>=1; 23 else a>>=1; 24 else if (~b&1) b>>=1; 25 else { 26 if (a<b) a^=b,b^=a,a^=b; 27 long long t=b; 28 b=(a-b)>>1; 29 a=t; 30 } 31 return d*(b?b:a); 32 }
/**********************扩展欧几里得算法***********************/
用这个算法可以求 X mod b=m 这种模线性方程(求的是X的通解,整数解)。变形一下,得 X+by=m,其中y为整数。通常写成 ax+by=m 的形式,然后解得x的通解,进而得到X=ax的通解。
其实就是在求GCD(a,b)的同时把解出满足 ax+by=m 的(x,y)通解。
首先,a和b的线性组合能表示的最小的正整数为GCD(a,b),也就是说
ax+by=m,m的最小值为GCD(a,b)
这个我目前不会证。。但是有用。
ax+by=m有整数解的前提是 GCD(a,b)|m
设d=GCD(a,b)
用EXGCD(待会儿再来讲这个函数)可以解出ax+by=d的一个整数解(x‘,y’),则
(x‘*m/d,y’*m/d)就是ax+by=m的一个整数解。
令(x0,y0)=(x‘*m/d,y’*m/d)
那么,有(x0+b/d*t,y0-a/d*t)是ax+by=m的通解。
小证一下:
由已知得ax0+by0=m
将(x,y)=(x0+b/d*t,y0-a/d*t)代入ax+by,化简一下就有
ax0+ab/d*t+by0-ab/d*t=ax0+by0=m
证毕。
所以只要得到ax+by=m的一组解,就可以用(x,y)=(x0+b/d*t,y0-a/d*t)得到满足要求的解。
一般要求的是满足条件的最小正整数x。可以用x=(x0 mod (b/d) + (b/d) ) mod (b/d)得到。理解?真聪明。。。
如果求的是X mod b=m的最小正整数解,则X=(ax mod b + b) mod b。这么写是为了避免a为负数的情况下造成的错误。
然后,大家肯定很纳闷这个(x',y')到底怎么求,接下来就讲的是用EXGCD 解 ax+by=d的一个特解的求法。
原来说过的,GCD(a,b)=GCD(b,a mod b)。
那么有
ax+by=d………………………………………………(1)
a'x'+b'y'=d…………………………………….........(2)
其中(a',b')=(b,a mod b)
可设 a=bk+t……………........………………………(3)
将(3)代入(1),有
(bk+t)x+by=d
整理,得
b(kx+y)+tx=d………………………………(4)
对比(2)和(4),且用(a',b')替换(b,t),有
a'x' + b'y' =d
a'(kx+y) + b'x =d
对比两式的系数,有
(x',y')=(kx+y,x)
整理下,有
(x,y)=(y',x'-ky')
也就是说,当深层的(x',y')解出后,就能推出外层的(x,y)
边界条件是
b=0时 d=a
那么 ax+by=d 的解(x,y)=(1,0)
用EXGCD就能递归地解得 ax+by=d 的一个特解 (x',y')
这么说,你们晕了吗?没晕?真聪明。。。。
那就看代码吧!
1 //Extended GCD 2 long long EXGCD(const long long &a,const long long &b,long long &x,long long &y) { 3 if (!b) { 4 x=1; 5 y=0; 6 return a; 7 } 8 long long d=EXGCD(b,a%b,y,x); 9 y-=a/b*x; 10 return d; 11 } 12 13 //main里面这么写 14 if (m%(d=EXGCD(a,b,x,y))) printf("No Solution\n"); 15 else { 16 x=x*(m/d) % (b/d);//防止x过大,使a*x爆long long 17 X=a*x % b; 18 printf("%lld\n",X<0?X+b:X);//之所以不用上面所讲的计算方法来求这个东西,是因为那么写太多模运算了。。看着烦。。而且感觉会比较慢。。 19 }
/**********************中国剩余定理***********************/
听到这名字觉得好光荣呃~~~
这也就是经常听到的孙子定理啦,韩信点兵啦什么的。。其实是一个东西。
问题是,求满足以下模线性方程组的 X(通常求的是X的最小正整数解)
X mod m1=r1
X mod m2=r2
...
...
...
X mod mn=rn
其中,mi之间两两互质
这么解:
首先 令 M=m1*m2*...*mn
对于每一个i,解 (M/mi)x+(mi)y=ri的解(x,y)
因为,mi间两两互质,那么M/mi和mi肯定是互质的,也就是说GCD(M/mi,mi)=1,方程必然有解。
这样,得到一个对应的Xi=(M/mi)*x
这个Xi有这么几个性质:
1、Xi mod mi = ri
方程变形一下就知道了。
(M/mi)x+(mi)y=ri
<==>(M/mi)x=ri-(mi)y1
<==>(M/mi)x mod mi = ri
<==>Xi mod mi = ri
显然吧?真聪明。。。。(真的很显然。。。)
2、Xi mod mj = 0 (i!=j)
因为M/mi=m1*m2*..*m(i-1)*m(i+1)*...*mn,
也就是说,M/mi是由除了mi以外的m连乘所得,必然有
mi | M/mi
所以 mi | (M/mi)*x
即 mi | Xi
于是,我们令SumX=X1+X2+...+Xn,那么这个 SumX 必然是满足条件的一个解。。稍微想想。能理解吧?
SumX的连加项中,只有一项Xi满足 Xi mod mi = ri,其他项Xj满足 mi | Xj (i!=j)。。懂了吧?真聪明。。。
因为每一个Xi都是存在的(刚才证明过了,GCD(M/mi,mi)=1),所以SumX必然是存在的,也就是说,!!!这种mi两两互质的情况下肯定是有解的。!!!(注意哈!)
那么就构造出一个解SumX了。。
接下来,显然 X=SumX+k*M (k为整数) 也是方程组的解。(因为mi | M)
所以类似的,我们要得到X的最小正整数解,就可以像讲EXGCD一样的方法求了。
X%=M;
if (X<0) X+=M;
因为没有碰到中国剩余定理的裸题。。所以就没有程序了。。。。好吧?真聪明。。。。
不知道你注意到没有。。用中国剩余定理有一个前提条件,就是mi之间是两两互质的!知道我什么意思吧?真聪明。。。
如果mi并不满足两两互质的话。。怎么解呢?
说实话我也不知道为什么没有两两互质的情况下,除了无解的情况,中国剩余定理在解方程的时候会出现问题。。虽然我试过,当mi之间成倍数关系时,中国剩余定理没办法解,否则好像是可以解的。而且判无解,中国剩余定理是能判的。也就是 GCD(a,b) | m 不满足的时候,无解。
Next...
/**********************一般模线性方程组***********************/
同样是求这个东西。。
X mod m1=r1
X mod m2=r2
...
...
...
X mod mn=rn
首先,我们看两个式子的情况
X mod m1=r1……………………………………………………………(1)
X mod m2=r2……………………………………………………………(2)
则有
X=m1*k1+r1………………………………………………………………(*)
X=m2*k2+r2
那么 m1*k1+r1=m2*k2+r2
整理,得
m1*k1-m2*k2=r2-r1
令(a,b,x,y,m)=(m1,m2,k1,k2,r2-r1),原式变成
ax+by=m
熟悉吧?
此时,因为GCD(a,b)=1不一定成立,GCD(a,b) | m 也就不一定成立。所以应该先判 若 GCD(a,b) | m 不成立,则!!!方程无解!!!。
否则,继续往下。
解出(x,y),将k1=x反代回(*),得到X。
于是X就是这两个方程的一个特解,通解就是 X'=X+k*LCM(m1,m2)
这个式子再一变形,得 X' mod LCM(m1,m2)=X
这个方程一出来,说明我们实现了(1)(2)两个方程的合并。
令 M=LCM(m1,m2),R=r2-r1
就可将合并后的方程记为 X mod M = R。
然后,扩展到n个方程。
用合并后的方程再来和其他的方程按这样的方式进行合并,最后就能只剩下一个方程 X mod M=R,其中 M=LCM(m1,m2,...,mn)。
那么,X便是原模线性方程组的一个特解,通解为 X'=X+k*M。
如果,要得到X的最小正整数解,就还是原来那个方法:
X%=M;
if (X<0) X+=M;
这么一来~~大功告成~~
中国剩余定理的题貌似都可以直接用这种方法来求了。。所以不用再特地敲中国剩余定理了。。。
代码~~~~~
1 long long EXGCD(const long long &a,const long long &b,long long &x,long long &y) { 2 if (!b) { 3 x=1; 4 y=0; 5 return a; 6 } 7 long long d=EXGCD(b,a%b,y,x); 8 y-=a/b*x; 9 return d; 10 } 11 //Modular Linear Equations 12 //虽然它不是中国剩余定理。。。但是还是喜欢把它命名为CRT——Chinese Remainder Theorem 13 long long CRT(long long *m,long long *r,const int &n) { 14 long long M=m[1],R=r[1],x,y,d; 15 for (int i=2; i<=n; i++) { 16 if ( (r[i]-R)%(d=EXGCD(M,m[i],x,y)) ) return -1; 17 x=(r[i]-R)/d*x%(m[i]/d);//处理x过大。。 18 R+=x*M; 19 M=M*m[i]/d; 20 R%=M; 21 } 22 return R>0?R:R+M; 23 }