扩展欧几里得算法

发现很多以前学过的都没有整理。。

扩展欧几里得算法用于求解二元一次不定式的通解,因而被广泛使用。

设定一常数为gcd=(A,B)
令AX0+BY0=gcd(A,B)(1),

如果完成欧几里得的算法后,发现:当B=0时,gcd(A,B)=A。要满足(1)式,显然x=1,y=0。此时,把递推过程完成,得到了一组结果。但是要求得出AX+BY=gcd(A,B)这种式子的满足的根,这样还不够,因为此时的A,B不是我们输入的A,B了,这时就要利用递归过程来实现。要找到上层递归与下层递归之间的关系,通过递归的方式使A,B回到最初的值,那个时候的等式得出的X,Y才是我们需要的。

设X,Y为当前嵌套中的值,X1,Y1为之前嵌套中的值。
就有等式AX+BY=gcd(A,B)=gcd(B,A%B)=BX1+(A-(A/B)B)Y1。(A-(A/B)B=A%B)。
整理式子得AX+BY=AY1+B(X1-(A/B)Y1)
这时,我们得出了关系式:X=Y1,Y=X1-(A/B)Y1。
模板如下:

ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll r=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return r;
}

返回值r是最大公约数,顺带着求出了x和y的值。

得出AX+BY=C的解,还有一些步骤
(1)要AX+BY=C有整数解,必须要满足C%gcd(A,B)=0,也就是C必须是gcd(A,B)的整数倍,X,Y才能乘以相应倍数,得到正确的解。
加一行代码判断是否有整数解:

if(c%exgcd(a,b)) 
puts("sorry");
else 
cout<<x<<" "<<y<<endl;

(2)将x=x(c/gcd(a,b)),y=y(c/gcd(a,b)),求出一组特殊解,就是平时默认的那种最小解。

x=x*(c/exgcd(a,b));
y=y*(c/exgcd(a,b));//X,Y乘以相应的倍数得到特解。

(3)这样还不能求多解,只能求出一组特殊解,再加入x=x+b,y=y-a。因为AX+BY=AX+AB+BY-AB
整理得AX+BY=A(X+B)+B(Y-A),推出X=X+B,Y=Y-A。

x=x+b;
y=y-a;

来一道题:

Codeforces 7C Line

求方程 Ax + By + C = 0的一组解,默认就是特解了。

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll r=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return r;
}
int main(){
    ios::sync_with_stdio(false);
    ll a,b,c;
    while(cin>>a>>b>>c)
    {
        ll x,y;
        ll gcd=exgcd(a,b,x,y);
        if(c%gcd!=0)
            puts("-1");
        else
        {
            ll k=-1*c/gcd;
            x*=k;
            y*=k;
            cout<<x<<" "<<y<<endl;
        }
    }
    return 0;
}

再来一个得出多组解的版本:

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll r=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
    return r;
}
int main(){
    ios::sync_with_stdio(false);
    ll a,b,c;
    while(cin>>a>>b>>c)
    {
        ll x,y;
        ll gcd=exgcd(a,b,x,y);
        int sum=10;//要得到多少组解
        if(c%gcd!=0)
            puts("-1");
        else
        {
            ll k=-1*c/gcd;
            x*=k;
            y*=k;
            while(sum--)
            {
                cout<<x<<" "<<y<<endl;
                x+=b;
                y-=a;
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值