1.朴素欧几里得:辗转相除法求gcd(a,b)(a和b的最大公约数),gcd(a,b)=gcd(b,a%b)
gcd(a,b) = gcd(b,amod b) (不妨设a>b 且r=a mod b ,r不为0)
a可以表示成a = kb + r(a,b,k,r皆为正整数),则r = a modb
假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。
而r= a - kb,两边同时除以d,r/d=a/d-kb/d=m,等式左边可知m为整数,因此d|r
因此d也是(b,a mod b)的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。
2.拓展欧几里得:必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。
首先证明这个命题成立
设a>b
(1)b=0时,gcd(a,b)=a,x=1,y=0
(2)ab≠0 gcd(a,b)=ax+by
同理存在gcd(b,a%b)=bx1+(a%b)y1
∵gcd(a,b)=gcd(b,a%b)
∴ax+by=bx1+(a%b)y1
ax+by=bx1+(a-(a/b)*b)y1 这一步特殊而重要,这里的“/”指的是计算机中的除并向下取整
ax+by=ay1+b(x1-(a/b)y1)
因为这个等式恒成立,所以x=y1,y=x1-(a/b)y1,
又因为当b=0时,x=1,y=0,这样就可以确保递推能求解x,y了,得证
上述程序只是得到了一组解,很显然解是不唯一的:x增加b, y减少a一定是原方程的一组解:
a * (x + b) + b * (y - a) =a * x + b * y = (a, b)。
然而在应用上,往往并不是如此简单,很多时候会求解不定方程a * x + b * y =n。这个时候还是应用上面的算法:
(1)求(a,b), 设c = (a,b),如果! c|n,则不存在整数解。因为将上式左右两边都除以c,可以知道,左边为整数,右边为非整数,故矛盾。
(2)将左右两边同时除以c,设得到新的方程为a' * x + b' * y = n',应用上述算法求a' * x + b' * y = 1的解(由第一步知道(a',b') = 1)。设结果为x', y'。
(3)x = x' * n' , y = y' * n'是方程a * x + b * y =n。这个比较好理解,将a' * x + b' * y= 1两边同时扩大n'倍就行了。
(4)x = x' * n' + t * b, y = y' * n' - t * a(t为整数)是原方程a * x + b * y =n的所有解。
应用:
1.用扩展gcd解线性方程ax+by=c
问题:ax+by=c,已知a、b、c,求解使该等式成立的一组x,y。其中a、b、c、x、y均为整数
a,b的最大公约数为gcd(a,b)。如果c不是gcd(a,b)的倍数,则该等式无解,因为等式左边除以gcd(a,b)是整数,而等式右边除以gcd(a,b)后为小数。
因此,只有当c是gcd(a,b)的倍数的时候,该等式有解。这样,可以通过计算使ax1+by1=gcd(a,b)成立的x1、y1,然后有x=(c/gcd(a,b))*x1,y=(c/gcd(a,b))*y1,得到x,y。
问题就被转换为求使ax+by=gcd(a,b)成立的一组x,y。这可以用扩展欧几里德算法求解。如下:
如果b为零,则gcd(a,b)=a,那么x=1,y=0为一组解。
如果b不为零,根据欧几里德定理,可以设
ax1+by1=gcd(a,b)=gcd(b,a%b)=bx2+(a%b)y2=bx2+(a-(a/b)*b)y2
化简后有x1=y2,y1=x2-(a/b)y2。因此x1,y1依赖于x2,y2,同理依次类推递归调用求出x3,y3,x4,y4……,类似于辗转相除,直到b=0时,求出xn,yn,便可以推出x1,y1的值。
2.扩展gcd解同余方程(把同余方程转为线性方程)
NOIP2012d2t1 同余方程 ax ≡ 1 (mod b) 最小的整数解x。
满足a*x≡1 (mod p)的x值
对于正整数 和 ,如果有 ,那么把这个同余方程中 的最小正整数解叫做 模 的逆元。
逆元求法
3.方程组的情形(中国剩余定理)
中国剩余定理介绍
在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3 余2),
五五数之剩三(除以5 余3),七七数之剩二(除以7 余2),问物几何?”这个问题称为“孙
子问题”,该问题的一般解法国际上称为“中国剩余定理”。具体解法分三步:
找出三个数:从3 和5 的公倍数中找出被7 除余1 的最小数15,从3 和7 的公倍数中找出被 5 除余1 的最小数21,最后从5 和7 的公倍数中找出除3 余1 的最小数70。
用15 乘以2(2 为最终结果除以7 的余数),用21 乘以3(3 为最终结果除以5 的余数),同理,用70 乘以2(2 为最终结果除以3 的余数),然后把三个乘积相加(15*2+21*3+70*2)得到和233。用 233 除以 3,5,7 三个数的最小公倍数 105,得到余数 23,即 233%105=23。这个余数 23 就是符合条件的最小数。就这么简单。我们在感叹神奇的同时不禁想知道古人是如何想到这个方法的,有什么基本的数学依据吗?
中国剩余定理分析
我们将“孙子问题”拆分成几个简单的小问题,从零开始,试图揣测古人是如何推导出
这个解法的。
首先,我们假设n1 是满足除以3 余2 的一个数,比如2,5,8 等等,也就是满足3*k+2
(k>=0)的一个任意数。同样,我们假设 n2 是满足除以 5 余 3 的一个数,n3 是满足除以 7 余2 的一个数。
有了前面的假设,我们先从n1这个角度出发,已知n1满足除以3余2,能不能使得 n1+n2 的和仍然满足除以3 余2?进而使得n1+n2+n3 的和仍然满足除以3 余2?
这就牵涉到一个最基本数学定理,如果有a%b=c,则有(a+kb)%b=c(k 为非零整数),换句话说,如果一个除法运算的余数为 c,那么被除数与 k 倍的除数相加(或相减)的和(差)再与除数相除,余数不变。这个是很好证明的。
以此定理为依据,如果n2 是3 的倍数,n1+n2 就依然满足除以3 余2。同理,如果n3
也是3 的倍数,那么n1+n2+n3 的和就满足除以3 余2。这是从n1 的角度考虑的,再从n2, n3 的角度出发,我们可推导出以下三点:
为使n1+n2+n3 的和满足除以3 余2,n2 和n3 必须是3 的倍数。
为使n1+n2+n3 的和满足除以5 余3,n1 和n3 必须是5 的倍数。
为使n1+n2+n3 的和满足除以7 余2,n1 和n2 必须是7 的倍数。
因此,为使n1+n2+n3 的和作为“孙子问题”的一个最终解,需满足:
n1 除以3 余2,且是5 和7 的公倍数。
n2 除以5 余3,且是3 和7 的公倍数。
n3 除以7 余2,且是3 和5 的公倍数。
所以,孙子问题解法的本质是从5 和7 的公倍数中找一个除以3 余2 的数n1,从3 和7
的公倍数中找一个除以5 余3 的数n2,从3 和5 的公倍数中找一个除以7 余2 的数n3,再将三个数相加得到解。在求n1,n2,n3 时又用了一个小技巧,以n1 为例,并非从5 和7 的公倍数中直接找一个除以3 余2 的数,而是先找一个除以3 余1 的数,再乘以2。
这里又有一个数学公式,如果a%b=c,那么(a*k)%b=(a%b+a%b+„+a%b)%b=(c+c+„+c)%b=kc%b(k>0),
也就是说,如果一个除法的余数为c,那么被除数的k 倍与除数相除的余数为kc。展开式中已证明。
最后,我们还要清楚一点,n1+n2+n3 只是问题的一个解,并不是最小的解。如何得到最
小解?我们只需要从中最大限度的减掉掉3,5,7 的公倍数105 即可。道理就是前面讲过的定理“如果a%b=c,则有(a-kb)%b=c”。所以(n1+n2+n3)%105 就是最终的最小解。
总结经过分析发现,中国剩余定理的孙子解法并没有什么高深的技巧,就是以下两个基本数学定理的灵活运用:
如果 a%b=c , 则有 (a+kb)%b=c (k 为非零整数)。
如果 a%b=c,那么 (a*k)%b=kc%b (k为大于零的整数)。
例如下面的一元线性同余方程组:
x ≡ a1 (mod m1)
x ≡ a2 (mod m2)
x ≡ a3 (mod m3)
… …
x ≡ an (mod mn)
假设整数m1, m2, m3……, mn两两互质,m=m1*…*mn,则对于任意的整数a1, a2, a3….an, X有解。
求解X过程:X=(除以m1余a1的a2,a3…an的公倍数+…+除以mj余aj的m1…mj-1, mj+1…mn的公倍数+除以mn余an的m1,…, mn-1的公倍数)%LCM(m1,…, mn)=[(除以m1余a1的a2,a3…an的公倍数)%LCM(a1,…,an)+…+(除以mj余aj的m1…mj-1, mj+1…mn的公倍数)%LCM(m1,…, mn)+(除以mn余an的m1,…, mn-1的公倍数) %LCM(m1,…,mn)]%LCM(m1,…, mn)。
问题转换为:找到除以mj余aj的m1…mj-1, mj+1…mn的公倍数。这里有个技巧,先找到除以mj余1的m1…mj-1, mj+1…mn的公倍数Nj,然后Nj*aj即可。
问题转换为:求Nj(m1…mj-1, mj+1…mn的最小公倍数Mj=m/mj,因为余数m1…mn互为质数,所以Mj是最小公倍数)。
问题转换为:求Mj的某个倍数Nj=Mj*R,使得除以mj余1。
问题转换为:求R,使得Mj*R≡1(mod mj),即求Mj的乘法逆元。
求乘法逆元(用扩展欧几里得算法):Mj*R+mj*y=1,变量是R和y,调用函数exGcd(Mj,mj,R,y)即可。
最终X=西格玛(Rj*Mj)*a[j]%m
代码,变量名和分析过程使用的一致:
long longexGcd(long long a,long long b,long long &x,long long &y){
if(0==b) {
x=1;y=0;return a;
}
long long gcd=exGcd(b,a%b,y,x);
y-=a/b*x; return gcd;
}
long long lmes(){
long long X=1,R,y,X,Mi;
for(int i=0;i<n;i++) m*=a[i];//n[i]是第i个余数
for(int i=0;i<n;i++) {
Mi=m/n[i];
exGcd(Mi,a[i],R,y);
X+=(a[i]*Mi*R)%m;
}
return (X+m)%m;//X可能是负数
}
m1,…,mn不互质的情形:合并方程。
long long Rem(longlong a[],long longm[],int num){
long longn1=n[0],a1=a[0],n2,a2,k1,k2,x0,gcd,c;
for(int i=1;i<num;i++){
n2=n[i],a2=a[i];
c=a2-a1;
gcd=exGcd(n1,n2,k1,k2);//解得:n1*k1+n2*k2=gcd(n1,n2)
if(c%gcd){
flag=1;
return 0;//无解
}
x0=c/gcd*k1;//n1*x0+n2*(c/gcd*k2)=c PS:k1/gcd*c错误!
t=n2/gcd;
x0=(x0%t+t)%t;//求n1*x0+n2*y=c的x0的最小解
a1+=n1*x0;
n1=n2/gcd*n1;
}
return a1;
}
21:50:03
游来游去 2017/5/14 21:50:03
{这一步通过exgcd求得逆元k1
游来游去 2017/5/14 21:51:05
用x0存k1
游来游去 2017/5/14 21:51:39
k1=K ( mod n2/d)
游来游去 2017/5/14 21:51:58
K=k1 mod (n2/d)
21:52:12
游来游去 2017/5/14 21:52:12
下面的程序就是这样实现的。}
http://acm.nyist.net/JudgeOnline/problem.php?pid=144小珂的苦恼
HDU 5512
pku2115
pku2891
pku1061
HDU 1370
HDU5768 Lucky7 数论
HDU 1573 X问题
pku2142
HDU 4675 GCD ofSequence (2013多校7 1010题数学题)
HDU 4655 CutPieces(2013多校6 1001题简单数学题)