前言
说实话我的数论一直学得不是很好,证明和很多符号都加大了我在学习过程中的困难程度。所以在写这一篇总结的时候我也觉得自己会出现这样或那样的小错误。所以当正在看这篇文章的你,如何发现了任何错误,都欢迎私信或评论指出。
gcd
要知道扩展欧几里得是什么,首先要知道欧几里得是什么。欧几里得算法又称辗转相除法,它是指两个正整数a,b的最大公约数。
他的计算公式为gcd(a,b)=gcd(b,a mod b);
也就是说,我们可以假设
a=∑ipiei
其中pi表示a的质因子,ei表示此质因子的最大次方。
b也同理。
就比如
12=22+31
、
24=23+31
。
也可以这么表示:
gcd(a,b)=∑i∈Sa∩Sbpimin(Ea[i],Eb[i])
最大公约数d为
max(d|a,d|b)
如何证明这个
计算公式是
正确的?
我们假设
a=kb+r
,则有
r=a−kb=amodb
。其中
k=⌊ab⌋
再假设
d
为
a,b
的一个公约数
则有
d|a,d|b
。
因为
r=a−kb
所以
d|b,d|a−kb
所以
d|b,d|amodb
那么gcd的时间复杂度是多少呢?
对于两个数
a
和
b
,我们要求他们的gcd,我们认为
a>=b
可以分类讨论:
1°
当
b<a2
时,每次gcd必将变小一半。
2°
当
b>=a2
时,
amodb=a−b<a2
也会一半一半的变小
所以gcd的时间复杂度是
log
的
Exgcd
现在我们知道了欧几里得算法,接下来我们看看扩展欧几里得算法(exgcd)。
对于任意两个数
a,b
,存在
x,y
使得
ax+by=gcd(a,b)
同时我们设
ax1+by1=gcd(a,b)
ax1+by1=gcd(b,amodb)
gcd(b,amodb)=b⋅x2+amodb⋅y2
假设我们已知
x2,y2
则
gcd(b,amodb)=b⋅x2+amodb⋅y2
gcd(b,amodb)=b⋅x2+⌊ab⌋⋅y2
gcd(b,amodb)=a⋅y2+(x2−⌊ab⌋⋅y2)⋅b
所以
a⋅x1+b⋅y1=gcd(a,b)=a⋅y2+(x2−⌊ab⌋⋅y2)⋅b
;
我们可以取
x1=y2,y1=x2−⌊ab⌋⋅y2
这样子我们就可以通过递归来实现扩展欧几里得啦!!!!!
至于递归的过程,我们知道
gcd(x,0)=x
,所以我i们a只需在递归底层把
x
赋为1,
y
赋为0,就可以不断求出前面的一组解,最后推出答案。
代码实现
void exgcd(int a,int b,int &x,int &y)
{
if (b==0)
{
x=1;y=0;
return;
}
exgcd(b,a%b,x,y);
int xx=y;
int yy=x-a/b*y;
x=xx;y=yy;
}
因为这是一个二元一次方程,所以存在多解,那么我们要想想怎么求解的集合?
我们假设
d=gcd(a,b)
则
ax+by=d
ad⋅x+bd⋅y=1
设
a′=ad
,
b′=bd
得出
a′x+b′y=1
如果要使
a′(x+Δx)+b′(y+Δy)=1
则
a′Δx+b′Δy=0
,
′Δx=−b′Δy
可以证明
a′|Δy,b′|Δx
所以对于该方程的解
x0,y0
,我们可以推出另一解为
x0+k⋅bgcd(a,b),y0−k⋅agcd(a,b)
后记
其实扩展gcd一点也不难,只是要把证明什么都看懂,其实理解起来就很容易了。注:如前言所说,如果你发现本篇文章有任何错误,欢迎私信或评论指出!