数论初步:辗转相除法和扩展欧几里得

1.辗转相除法

虽然很久以前就知道这个方法了,但是一直都不明白原理【汗】
我们假设 G C D ( x , y ) GCD(x,y) GCD(x,y)为x,y的最大公因数,那么有这样的一个结论:
x>=y时: G C D ( x , y ) = G C D ( x m o d    y , y ) GCD(x,y)=GCD(x \mod y,y) GCD(x,y)=GCD(xmody,y)(如果x比y小则x mod y还是等于x,不会有影响)
证明如下:
令x,y的最大公因数为n,则 x ∣ n , y ∣ n x|n,y|n xn,yn x m o d    y x \mod y xmody 等同于 x − y ∗ k x-y*k xyk, 因为 x ∣ n , y ∣ n x|n,y|n xn,yn所以 x m o d    y x \mod y xmody也被n整除,则此时x,y的公因数也是 x m o d    y x \mod y xmody,y的公因数,因为n时x,y的最大公因数,所以n也是 x m o d    y x \mod y xmody,y的最大公因数。

有了如上这个结论我们就可以愉快地递推了。
但是为什么是“辗转"相除呢,因为这样就能够避免我上面写到的x比y小的问题。我们每次交换x,y的位置,就能让它不停地除下去。
代码如下:

int gcd(int x,int y){
	if (y==0) return x; else return gcd(y,x%y);
}

扩展欧几里德算法(exgcd)

辗转相除法又被称为欧几里德算法,那么扩展欧几里德算法就是在欧几里德算法上稍加改动得到的。
exgcd用于解决形如 a x + b y = G C D ( a , b ) ax+by=GCD(a,b) ax+by=GCD(a,b)这样的不定方程求特殊根的问题(求出一个特殊根 x 0 , y 0 x0,y0 x0,y0就可以用公式 x = x 0 + b G C D ( a , b ) ∗ t , y = y 0 − a G C D ( a , b ) ∗ t x=x0+\frac b {GCD(a,b)}*t,y=y0- \frac a {GCD(a,b)}*t x=x0+GCD(a,b)bt,y=y0GCD(a,b)at来求出所有的根)
其实辗转相除法与不定方程有很深的关系:
辗转相除最后的答案必定是 a = G C D ( a , b ) , b = 0 a=GCD(a,b),b=0 a=GCD(a,b),b=0.
带入方程 a x + b y = G C D ( a , b ) ax+by=GCD(a,b) ax+by=GCD(a,b),可求得x=1,y=0。
那么可以表示为: a ∗ 1 + b ∗ 0 = G C D ( a , b ) a*1+b*0=GCD(a,b) a1+b0=GCD(a,b)
对于某一个 x 1 , y 1 x1,y1 x1,y1,满足: a x 1 + b y 1 = G C D ( a , b ) ax1+by1=GCD(a,b) ax1+by1=GCD(a,b)
那么必定存在另一组x2,y2满足: b ∗ x 2 + ( a m o d    b ) y 2 = G C D ( b , a m o d    b ) b*x2+(a \mod b)y2=GCD(b,a\mod b) bx2+(amodby2=GCD(b,amodb)(这一项即为辗转相除法求得的下一项)
由上面的结论: G C D ( x , y ) = G C D ( x m o d    y , y ) GCD(x,y)=GCD(x\mod y,y) GCD(x,y)=GCD(xmody,y)
可得: a ∗ x 1 + b ∗ y 1 = b ∗ x 2 + a m o d    b y 2 a*x1+b*y1=b*x2+a \mod by2 ax1+by1=bx2+amodby2
这个等式也可以写成: a ∗ x 1 + b ∗ y 1 = b ∗ x 2 + ( a − a b ∗ b ) ∗ y 2 a*x1+b*y1=b*x2+(a-\frac a b*b)*y2 ax1+by1=bx2+ababy2
变形: a ∗ x 1 + b ∗ y 1 = a ∗ y 2 + b ∗ x 2 − a b ∗ b ∗ y 2 = a ∗ y 2 + b ∗ ( x 2 − a b ∗ y 2 ) a*x1+b*y1=a*y2+b*x2-\frac a b*b*y2=a*y2+b*(x2-\frac {a} {b}*y2) ax1+by1=ay2+bx2baby2=ay2+b(x2bay2)
那么就可以发现: x 1 = y 2 , y 1 = x 2 − a b ∗ y x1=y2,y1=x2-\frac a b*y x1=y2,y1=x2bay
又因为我们确定了边界(1,0),所以可以递推解决。

代码如下:

void exgcd(int a,int b,int &x,int &y){
	if (b==0) {x=1,y=0; return;}
	exgcd(b,a%b,x,y);
	int t=x; x=y,y=t-a/b*y;
}//x,y就是所求的特殊解

扩展欧几里得算法求线性模方程的最小正整数解

我们先考虑一个线性方程, a x ≡ b ( m o d c ) ax≡b (mod c) axb(modc)
我们要求这个方程的一个最小正整数解。
那么 b b b一定能够表示成 c k + a x ck+ax ck+ax的形式,因为 a x ax ax b b b c c c同余。
那么就可以写成一个不定方程: a x + b y = c ax + by = c ax+by=c
这就可以用不定方程求解。
首先如果gcd(a,b)不是c的倍数,那么肯定无解。
否则,我们我们求一下exgcd(a,b)。
考虑通解的一个方程:
设x0,y0为一组特殊解,可知通解为:
x = c g c d ( a , b ) ∗ x 0 + b g c d ( a , b ) ∗ t x=\frac c {gcd(a,b)}∗x0+\frac b {gcd(a,b)}∗t x=gcd(a,b)cx0+gcd(a,b)bt
y = c g c d ( a , b ) ∗ y 0 – a g c d ( a , b ) ∗ t , t ∈ Z y=\frac c {gcd(a,b)}∗y0–\frac a {gcd(a,b)}∗t,t∈Z y=gcd(a,b)cy0gcd(a,b)at,tZ
那么根据这一式子,我们可以推出最小的一组正整数解: ( x 0 ∗ c g c d ( a , b ) m o d    b + b ) m o d    b (x0* \frac c {gcd(a,b)}\mod b+b) \mod b (x0gcdabcmodb+b)modb

例题:BZOJ1477 青蛙的约会:传送门
代码如下:

#include<cstdio>
#include<string>
#define ll long long
using namespace std;
ll x,y,m,n,L,a,b;
ll exgcd(ll x,ll y,ll &x0,ll &y0){
    if (y==0) {x0=1,y0=0; return x;}
    ll ret=exgcd(y,x%y,x0,y0);
    ll t=x0; x0=y0,y0=t-x/y*y0;
    return ret;
}
ll solve(ll a,ll b,ll c){
    ll x0,y0,g=exgcd(a,b,x0,y0);
    if (c%g) return -1;
    x0=x0*c/g; y0=y0*c/g;
    x0=(x0%b+b)%b;
    return x0;
}
int main(){
    scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&L);
    if (solve(n-m,L,x-y)==-1) printf("Impossible\n");
    else printf("%lld\n",solve(n-m,L,x-y));
    return 0;
}

update 2018.7.20 更新了latex公式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值