扩展欧几里得算法

对于 a ∗ x + b ∗ y = c a*x+b*y=c ax+by=c,这样一个二元一次方程组,我们想要得到他的一组解可以用扩展欧几里得算法,参数列表的a,b,x,y就是方程中的a,b,x,y,d计算出来是gcd(a,b)。

算法求出来的是 a ∗ x + b ∗ y = g c d ( a , b a*x+b*y=gcd(a,b ax+by=gcd(a,b的一组解,解系可以表示为 X = x + b / d ∗ k , Y = y − a / d ∗ k X=x+b/d*k,Y=y-a/d*k X=x+b/dk,Y=ya/dk,k为任意整数

但是我们要求解的是 a ∗ x + b ∗ y = c a*x+b*y=c ax+by=c

方程组有解的充分必要条件是 d ∣ c d|c dc,正确性很容易证明

在确定方程组优解以后 c / d ∗ X , c / d ∗ Y c/d*X,c/d*Y c/dX,c/dY为方程组的一组解系

需要注意的是这样得到的不是方程组的所有的解

例如:对于方程 3 x + y = 60 3x+y=60 3x+y=60, 17 ∗ 3 + 9 ∗ 1 = 60 17*3+9*1=60 173+91=60显然为一组解,但是我们却没有办法用过扩展欧几里得算法得到这组解。所以凡是对x,y有限制条件(不是指正负,而是要求和不能超过多少等条件)我们要考虑是否能够使用扩展欧几里得算法,相反,如果对x,y具体大小没有过多限制,而是仅仅说明要求正负等条件时就可以使用扩展欧几里得算法。

void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
    if(!b){d=a; x=1; y=0;}
    else {gcd(b,a%b,d,y,x); y-=x*(a/b);}
}

下面简单证明一下正确性。

令c=a%b=a-k*b (k=a/b)
a ∗ x 1 + b ∗ x 2 = g c d ( a , b ) a*x1+b*x2=gcd(a,b) ax1+bx2=gcd(a,b) b ∗ x 2 + c ∗ y 2 = g c d ( b , c ) b*x2+c*y2=gcd(b,c) bx2+cy2=gcd(b,c)
有最大公因数的性质我们知道 g c d ( a , b ) = g c d ( b , c ) gcd(a,b)=gcd(b,c) gcd(a,b)=gcd(b,c)
所以我们得到等式 a ∗ x 1 + b ∗ y 1 = b ∗ x 2 + c ∗ y 2 = b ∗ x 2 + a ∗ y 2 − k ∗ b ∗ y 2 a*x1+b*y1=b*x2+c*y2=b*x2+a*y2-k*b*y2 ax1+by1=bx2+cy2=bx2+ay2kby2
其中的一组解为 x 1 = y 2 , y 1 = x 2 − k ∗ y 2 , 其 中 k = a / b x1=y2,y1=x2-k*y2,其中k=a/b x1=y2,y1=x2ky2,k=a/b,因此我们可以递归的求解,终止递归的条件为c==0,则这个时候的b为gcd(a,b),解为x=1,y=0,然后递归的返回(这个时候再看代码应该就能理解了,代码实现很巧妙)

为了快速得到非负的X,可以稍微处理一下:X=(X%B’+B’)%B’,这样得到的就应该是最小的非负X了
(B’=B/D)

简单的例题hihoCoder#1297

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<ctime>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;

typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e5+5;

ll s1,s2,v1,v2,m;

void ex_gcd(ll A,ll B,ll& D,ll& x,ll& y)
{
    if(!B)
    {
        D=A; x=1; y=0;
    }
    else
    {
        ex_gcd(B,A%B,D,y,x);
        y-=(A/B)*x;
    }
    
}

int main()
{
    while(~scanf("%lld%lld%lld%lld%lld",&s1,&s2,&v1,&v2,&m))
    {
        ll A=v1-v2,B=m,C=s2-s1,D,x,y;
        if(A<0)
        {
            A=-A; C=-C;
        }
        C=(C%m+m)%m;
        ex_gcd(A,B,D,x,y);
        if(C%D)
        {
            printf("-1\n");
            continue;
        }
        B/=D;
        x=C/D*x;
        x=(x%B+B)%B;
        printf("%lld\n",x);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值