拓展欧几里得算法
前置知识:欧几里得算法
欧几里得算法,是一个可以在 O(logmin(a,b)) 的时间内求出 gcd(a,b) 的算法。
而拓展欧几里得算法不仅可以在 O(logmin(a,b)) 的时间内求出 gcd(a,b),还可以求出一个二元一次不定方程 ax+by=gcd(a,b) 的一组解。
首先,我们来回顾一下欧几里得算法。
回顾
首先,gcd(a,b)=gcd(a−b,b),因为 gcd(a,b)∣a 且 gcd(a,b)∣b。
然后,gcd(a,b)=gcd(b,amodb),因为 amodb=a−b⌊ba⌋。
所以,gcd(a,b) 的值可以通过递归调用 gcd(b,amodb) 直到 a,b 至少有一个为 0。
因为 amodb<min(2a,b),所以至多会调用 logmin(a,b) 次。
我们可以类比欧几里得算法,定义 exgcd(a,b) 为返回 x,y,gcd(a,b) 三个值的函数。
它的算法流程是这样的:
- 如果 b=0,返回 x=1,y=0,gcd(a,b)=a。
- 设 a′=b,b′=amodb,递归调用 exgcd(a′,b′) 求出 x′,y′,gcd(a′,b′),其中 x′,y′ 为 a′x′+b′y′=gcd(a′,b′) 的一组解。
- 令 x=y′,y=x′−y′⌊ba⌋,返回 x,y,gcd(a′,b′)。
时间复杂度和欧几里得算法一样,是 O(logmin(a,b))。
为什么这样是对的呢?
证明
根据之前的结论,gcd(a′,b′)=gcd(a,b)。
又因为 ax+by=gcd(a,b) 且 a′x′+b′y′=gcd(a′,b′),所以 ax+by=a′x′+b′y′ (1)。
将 a,b 代入 a′,b′ 得 a′=b,b′=amodb=a−b⌊ba⌋。
再将 a′,b′ 代入 (1),得:
ax+byax+byax+by+b⌊ba⌋y′ax+b(y+⌊ba⌋y′)=bx′+(a−b⌊ba⌋)y′=bx′+ay′−b⌊ba⌋y′=bx′+ay′=ay′+bx′
所以,有:
xy+⌊ba⌋y′y=y′=x′=x′−⌊ba⌋y′
至此,算法正确性得证。
代码实现:
int exgcd(int a, int b, int &x, int &y){
if (b == 0){
x = 1, y = 0;
return a;
}
int ret = exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return ret;
}