扩展欧几里得(exgcd)的不才之论

关于exgcd(扩展欧几里得定理):

由扩展欧几里德定理,可以通过扩展欧几里德算法求解线性同余方程

扩展欧几里德定理(百度百科)

对于不完全为 0 的整数 a,b,gcd(a,b)表示 a,b 的最大公约数。那么一定存在整

数 x,y 使得 gcd(a,b)=ax+by。

即 ax +by = gcd(a, b); 一定存在整数解(x, y);

 

题设:

已知a,b,设方程组:ax + by = gcd(a, b) , 求一组(x, y)使得该式成立。

 

推导过程:

由欧几里得定理可知:

由:ax1+by1 = gcd(a, b) = gcd(b, a%b) = bx2 + (a%b)y2 ;

而 :bx2 + (a%b)y2  = bx2 + (a - a/b * b)y2 ;

整理可得:

x1a + y1b = y2a + (x2 - a/b*y2)b

由a, b的系数相同和待定系数法可知:

x1 = y2;

y1 = x2 - a/b*y2;

这样它就将a与b的线性组合化简为b与a%b的线性组合。

因为a和b都在不断减小,gcd(a, b) = gcd(b, a%b) 当b==0 时,a即为gcd(a, b);

所以,当b == 0;时,存在递归终点,此时,xn=1, yn=0;

然后递归回去就可以求出最终的x1和y1了

代码:

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

解出的(x1, y1)为ax + by = gcd(a, b)的一组解。

 

题设:

已知a,b,c,设方程组: ax + by = c, 求一个最小正整数解x,使得该方程成立。

 

推导过程:

由上述扩展欧几里得定理可知:c = n*gcd(a, b) (n为整数)

即原式变为:ax + by = gcd(a, b) *n (n为整数)。[即n = c/gcd(a, b)]

 

由上个题设exgcd可求得一组解(x1, y1)

即求得一组(x1, y1)使得 :

                                                 ax1 + by1 = gcd(a, b)                               ------------------------------------------------  (1)

令gcd = gcd(a, b);  

因为gcd = gcd(a, b) 所以, a*x1/gcd是整数,b*y1/gcd是整数, 所以c/gcd也需要是整数,否则无解。

 

让上述(1)式两边都乘(c/gcd)

得到:a*(x1*(c/gcd)) + b*(y1*(c/gcd)) = gcd * (c/gcd)

化简得:a*(x1*(c/gcd)) + b*(y1*(c/gcd)) = c       --------------------------  (2)

 

即原式中的解(x, y)可有exgcd后的一组解(x1, y1)表示为:

x = x1*(c/gcd)

y = y1*(c/gcd)

 

 

此时的x1*(c/gcd)是最小的解,但有可能是负数。

因为 a*(x1*(c/gcd) + b*n) + b*(y1*(c/gcd) - a*n) = c (n是自然数)

所,x的最小正整数解为:

x = (x1*(c/gcd)%b+b)%b

 

反过来可得最小的y的正整数解为:

y = (y1*(c/gcd)%a+a)%a

 

这里给出一个为什么x = (x1*(c/gcd)%b+b)%b是最小正整数的推导:

代码:

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
       x=1;y=0;
       return a;
    }
    int r=exGcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return r;
}
int main()
{
    int a, b, c;
    while(cin >> a >> b >> c && a+b+c)
    {
        int x, y;
        int z = exgcd(a, b, x, y);
        cout << x << ' ' << y << ' ' << z << '\n';
 
        x*=c/z;     //一组解x,y
        y*=c/z;
        cout << x << ' ' << y << '\n';
 
        int t = b/z;        //求ax+by = c 的最小正整数x,b/z是(2)式约掉c之后的。。
        x = (x%t+t)%t;
        y = abs((a*x - c)/b);
 
        t = a/x;             //求ax+by = c 的最小正整数y
        y = (y%t+t)%t;
        x = abs((b*y - c)/a);
        cout << x << ' ' << y << '\n';
    }
    return 0;

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值