『扩展欧几里得算法 Extended Euclid』
<更新提示>
<第一次更新>
<正文>
Euclid算法(gcd)#
在学习扩展欧几里得算法之前,当然要复习一下欧几里得算法啦。
众所周知,欧几里得算法又称gcd算法,辗转相除法,可以在O(log2b)时间内求解(a,b)(a,b的最大公约数)。
其核心内容可以陈述为:(a,b)=(b,a%b),然后反复迭代该式缩小a,b规模,直到b=0,得到a为最大公约数。
证明#
设两数为a b(b<a),求它们最大公约数的步骤如下:用b除a,即a/b=q…..r,得a=bq+r(0≤r<b),即为余数,q是这个除法的商)。若r=0,则b是a和b的最大公约数,a,b存在倍数关系。若r≠0,则继续考虑。
首先,应该明白的一点是任何 a 和 b 的公约数都是 r 的公约数。要想证明这一点,就要考虑把 r 写成 r=a−bq。现在,如果 a 和 b 有一个公约数 d,而且设 a=sd,b=td, 那么 r=sd−tdq=(s−tq)d。因为这个式子中,所有的数(包括 s−tq)都为整数,所以 r 可以被 d 整除。
对于所有的 d 的值,这都是正确的。所以 a 和 b 的最大公约数也是 b(因为b<a,所有取b继续运算才能不断缩小规模,直至两数有倍数关系) 和 r 的最大公约数。因此我们可以继续对 b 和 r 进行上述取余的运算。这个过程在有限的重复后,可以最终得到 r=0 的结果,我们也就得到了 a 和 b 的最大公约数
实现#
Code:
inline int Euclid(int a,int b){return b==0?a:Eucild(b,a%b);}
Extended Euclid算法(exgcd)#
那么接下来就是扩展欧几里得啦。
正如其名,扩展欧几里得算法就是基于欧几里得算法的扩展运用。该算法用于解决一下模型的问题:
求解关于x,y的二元不定方程ax+by=c的整数解
在讲解算法之前,需要先了解该算法的核心,即裴蜀定理:
对任何整数a,b,关于未知数x和y的线性丢番图方程(称为裴蜀等式):ax+by=c,方程有整数解当且仅当c是(a,b)的倍数。裴蜀等式有解时必然有无穷多个解。
证明#
1.必要性证明:如果有整数解,则c是p的倍数
令p=(a,b),设a=a′p,b=b′p,则有(a′,b′)=1成立。那么
那么c就是p的一个因数,所以p|c得证。
2.充分性证明:如果c是p的倍数,则ax+by=c有整数解
令p=(a,b),记欧几里得算法中每一次辗转得到的数对为(a1,b1),(a2,b2),...,(an,bn),其中,(a1,b1)即为(a,b),(an,bn)即为(p,0),符合辗转相除法的流程。
对于(an,bn),求方程anx+bny=c的解,由于an=p,bn=0,我们可以构造一组解:
适用于(an,bn),且(xn,yn)一定为整数(因为p|c,yn取任意整数)。
至此,数学归纳法的底基证明完毕。
由于辗转相除法,我们可以得知
我们刚才已经推得:anx+bny=c的一组整数解(xn,yn),那么可以把①②两式代入anx+bny=c得:
且对于该方程,解(xn,yn)仍适用,即:
成立,可以进行推导:
注意到两项的系数分别为an−1,bn−1,所以对于方程an−1x+bn−1y=c,通过③式可以直接得到一组解:
由此,我们利用辗转相除法的关系,通过方程anx+bny=c的一组解(xn,yn),推得了方程an−1x+bn−1y=c的一组解(xn−1,yn−1)。
同样地,我们可以由(xi,yi)的一组解,得到方程ai−1x+bi−1y=c的一组解(xi−1,yi−1)
由上,数学归纳法完成证明:如果我们得知anx+bny=c的一组解(xn,yn),且(a1,b1),(a2,b2),...,(an,bn)是由辗转相除法得到的序列,那么我们就可以通过以上方法得到原方程a1x+b1y=c的解(x1,y1)。
然而,我们已经通过构造法得到anx+bny=c的一组解(xn,yn),且保证c是p的倍数时,整数解(xn,yn)一定存在。故c是p的倍数时,方程ax+by=c一定有整数解。充分性得证。
实现#
回归正题,看扩展欧几里得算法。
千万不要想着不看证明咯。裴蜀定理的充分性证明过程就是扩展欧几里得算法的流程。
先由辗转相除法求解(a,b),得到p=(a,b)
同时,构造解(xn,yn)
在递归的回溯过程中,利用公式:
倒推每一组(ai,bi)的解(xi,yi)。
最后得到(a,b)和原方程ax+by=c的一组解(x,y)。
至此,扩展欧几里得算法完成。
Code:
#include<bits/stdc++.h>
using namespace std;
inline int Extended_Euclid(int a,int &x,int b,int &y,int c)
{
if(b==0){x=c/a,y=0;return a;}
else
{
int p=Extended_Euclid(b,x,a%b,y,c);
int x_=x,y_=y;
x=y_; y=x_-a/b*y_;
return p;
}
}
int main(void)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int p,x,y;
p=Extended_Euclid(a,x,b,y,c);
printf("(%d,%d)=%d\n",a,b,p);
printf("x=%d,y=%d\n",x,y);
}
<后记>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
· 通过一个DEMO理解MCP(模型上下文协议)的生命周期
· 工良出品 | 长文讲解 MCP 和案例实战
· 多年后再做Web开发,AI帮大忙
· centos停服,迁移centos7.3系统到新搭建的openEuler
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 上周热点回顾(4.14-4.20)