拓展欧几里得(易懂)

拓展欧几里得问题:

首先大家应该都知道,欧几里得算法吧也就是求最小公倍数gcd(a,b);而拓展欧几里得就是求解方程 ax+by=gcd( a,b )的所有整数解,既然这是拓展欧几里得所以核心和欧几里得算法类似。这一算法的核心就是利用递归求解x与y。
ok,既然是递归,那我们就应该知道

一:递归的原理(如何递归),

二:与递归需要维护的变量。

一,如何递归:

首先,如何递归就是指找到上一层与下一层的关系。我们知道欧几里得算法(以下用gcd(a,b)来表示),是利用递归思想找到了上一层与下一层之间的关系(还是附个板子吧)

void gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}

(哈哈哈,上一层与下一层已经确定即gcd(a,b)与gcd(b,a%b))
而且gcd(a,b)=gcd(b,a%b)…(1)很明显是成立的,所以:
还记得我们的问题吗? ax+by=gcd(a,b),以及确定上一层与下一层的关系(x1,x2,y1,y2之间的关系),那该怎么办呢??(=@__@=)
(巨巨们呵呵一笑,换元Orz。)没错就是换元。那么就可以得到下面三个式子(为了更容易理解特意分开写):

一:ax1+by1=gcd(a,b);
二:a*x2+(a%b)*y2=gcd(b,a%b);

利用 (1) 式得:(这里再指明一下当前的问题,找到上一层x1,y1,与下一层x2,y2的关系)

三:ax1+by1=a*x2+(a%b)*y2;

(这里特意用x1,x2,y1,y2来表示就是为了表明他们是上下层的关系,即找他们之间的关系,不要想当然→_→)
然后众所周知a%b=a-(a/b)*b,然后换元得:

四:a *x1 + b * y1 = b * x2 + (a - (a / b) * b) * y2;

还是看不出来啊QAQ,别急,翻一下高数书
(先提取系数再待定系数法哈哈,啦啦啦(˘︶˘).。)

五:右式=b * (x2 - (a / b) * y2) + a * y2;

带入四得:

六:a * x1 + b * y1 = a * y2 + b * (x2 - (a / b) * y2)

最后一步(系数相同),得到两个式子:

七:x1=y2;
八:y1 = x2 - (a / b) * y2

(手撸一遍,很快的)
不过为什么要写到这里? 当然是想知道如何递归啊,仔细观察 (七) 与 (八) 式,你看到了什么(…(⊙_⊙;)…,神奇吧),我们其实已经实现了一层递归,他们的关系已经找到。(这个问题停一下)

什么时候停止呢??这里回想一下gcd(a,b);随着递归,b最终会变成 0,此时的方程因为b=0,就变成 a*x=gcd(a,b); (STOP!!!)然后得到一个解x=1, y=0。然后开始回溯,就可以得到a * x + b * y = gcd(a,b)的解。(一个特解,由它推出来通解)(amazing~~~);
如何求出所有通解呢?
a * x + b * y = gcd(a,b),我们让x增加b/gcd(a,b),让y减少a/gcd(a,b),等式两边相等(自己展开一下就知道啦)
我们就可以通过这种操作来更改x,y的值。

二:与递归需要维护的变量。

额~~~,很明显了以经,像gcd一样维护a,b因为需要求解,x,y所以要维护一下x,y;(看一下代码吧)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll ex_gcd(ll a,ll b, ll &x,ll &y)///文章讲的版本
{
    if(a==0&&b==0)return -1;///无最大公约数
    if(b==0){x=1;y=0;return a;}
    ll g=ex_gcd(b,a%b,x,y),tmp=x;
    x=y;y=tmp-a/b*y;
    return g;
}
ll ex_gcd_kuangbin(ll a,ll b, ll &x,ll &y)///这种是kuangbin大佬的模板,他将x,y递归时换一下位置,然后再写一个式子即可(巨巨不愧是巨巨)。(本蒟蒻没看太懂,希望大佬指点)
{
    if(a==0&&b==0)return -1;///无最大公约数
    if(b==0){x=1;y=0;return a;}
    ll g=ex_gcd(b,a%b,y,x);///差别
    y-=a/b*x;
    return g;
}
int main()
{
    ll a,b,x,y;
    cin>>a>>b;
  //  int tem=lcd_gcd(a,b,x,y);
    int tem=ex_gcd2(a,b,x,y);
    printf("%d%d\n",x,y);
}

这里强调一下函数是调用x,y的地址,因为x,y最开始没有赋值!!!。

问题一般化:

上面是求a * x + b * y = gcd(a,b)的解,而更一般的是a * x + b * y = c;(c是一个常数),怎么求呢??
两种情况:
(1) 如果c % gcd(a,b) != 0,无解。
(2) 如果c % gcd(a,b) ==0,且c / gcd(a,b) = k,那么将求出的解 x,y 都乘上k,就是答案,即kx,ky, (细心地读者会看到原式可以转化为a * x + b * y = k * gcd(a,b) );
(巨巨:用膝盖都能想出来,Orz);

例题:

落谷p1082

题解:

需要先将原式处理一下变为1+by=ax;----->ax-by=1
由于本题是为了求解x所以可以直接转化为ax+by=1;(不影响)
然后正常求解就行(记得要乘以1/gcd(a,b),不懂的话好好把 问题一般化 读一下)

AC代码:
#include<bits/stdc++.h>
using namespace std;
int a,b,x,y;
int ex_gcd(int a,int b,int &x,int &y)
{
    if(a==0&&b==0) return -1;
    if(b==0){x=1;y=0;return a;}
    int g=ex_gcd(b,a%b,x,y),tmp=x;
    x=y;y=tmp-a/b*y;
    return g;
}
void sol(int gcd,int b)
{
    int k=1/gcd,tmp=b/gcd;
    while(x>0)
    {
        x-=tmp;
    }
    x+=tmp;///因为题目要求最小非负整数,所以也没必要把 k 换成double。
    x*=k;
    cout<<x<<endl;
}
int main()
{
    scanf("%d%d",&a,&b);
    int gcd=ex_gcd(a,b,x,y);
    sol(gcd,b);
}

总结:

第一次写这么长的文章(只算字),虽说挺耗时间,但是比只看博客理解的更深刻。也挺好,希望能够给更多人带来帮助(顺便涨点赞吧)。

告诫:

永远不要骗自己

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值