扩展GCD和线性模方程组

扩展GCD即求解ax+by=c(a,b,c一般为已知整数)此类的方程所用方法。

其精华部分就在于GCD那个函数部分。证明等详见百度百科。

先求ax+by=GCD(a,b) 保证有解

那么对于原方程:

有解条件:c mod GCD(a,b)=0

一组解:x*(c/GCD(a,b)),y*(c/GCD(a,b))。

这样,我们就能在至多O(logn)的时间内求出一组解了。

有一个显然的性质是:x加上b且y减去a,方程仍满足。

那么我们要取一个对于x(或y)的最小非负整数解时,就可以这样做:

x=(x mod b+b) mod b (b>0)

    (x mod b-b) mod b (b<0)

   即(x mod b+abs(b)) mod b

    b=0的话。。。这个就只有唯一解了。

那么对于求解线性模方程组,用中国剩余定理的话,有一定的局限性,即模数必须两两互质,而扩展GCD就没有这个限制了。

大致解法就是把方程合并,对于两个方程:

X mod a=b

X mod a'=b

设X=ak+b=a'k'+b'

则ak-a'k'=b'-b,a,-a',(b'-b)代入方程求出k,k'(同时判无解),然后算出X。

这样两个方程就合并成了X mod LCM(a,a')=X 很好理解,满足这一方程的X必定能同时满足原来的两个原方程,反过来也成立。这样就合并N-1次,求出最后的余数。

 

PKU1061

var x,y,n,m,L,gg,xx,yy,zz:int64;
function gcd(a,b:int64;var x,y:int64):int64;
var tmp:int64;
begin
	if b=0 then begin gcd:=a;x:=1;y:=0;end
	       else
		   begin
			gcd:=gcd(b,a mod b,x,y);
			tmp:=x;x:=y;y:=tmp-a div b*y;
		   end;
end;

begin
	readln(x,y,m,n,L);
	gg:=gcd(m-n,L,xx,yy);
	if ((y-x) mod L) mod gg<>0 then writeln('Impossible')
							   else
							   begin
								//writeln((((y-x) mod L) div gg*xx) mod L);
                                                                zz:=(((y-x) mod L) div gg*xx) mod L;
                                                                zz:=(zz+L) mod L;
                                                                writeln(zz);
							   end;
end.

 

线性模方程:

var t,l,n,i:longint;gg,x,y:int64;pd:boolean;
    a,b:array[1..10] of int64;
function gcd(a,b:int64;var x,y:int64):int64;
var tmp:int64;
begin
	if b=0 then begin gcd:=a;x:=1;y:=0;end
	       else
		   begin
				gcd:=gcd(b,a mod b,x,y);
				tmp:=x;x:=y;y:=tmp-a div b*y;
		   end;
end;

begin
	readln(t);
	for l:=1 to t do
	begin
		readln(n);
		for i:=1 to n do read(a[i]);readln;
		for i:=1 to n do read(b[i]);readln;
		pd:=true;
		for i:=2 to n do
		begin
			gg:=gcd(a[i-1],-a[i],x,y);
			if (b[i]-b[i-1]) mod gg<>0 then begin pd:=false;break;end;
                        b[i]:=x*((b[i]-b[i-1]) div gg)*a[i-1]+b[i-1];
			a[i]:=a[i-1]*a[i] div abs(gg);
                        b[i]:=(b[i] mod a[i]+a[i]) mod a[i];
		end;
                write('Case ',l,': ');
		if pd then if b[n]=0 then writeln(a[n]) else writeln(b[n])
		      else writeln(-1);
		
	end;
end.
posted @ 2011-08-02 10:04  FancyCoder0  阅读(819)  评论(0编辑  收藏  举报