欧几里得算法gcd以及拓展欧几里得算法exgcd
欧几里得算法,又叫辗转相除法,我更愿意叫辗转相除法,因为这更能体现他的原理
求a和b的最大公因数
我们假设
a% b == c1;
b% c1 == c2;
c1 % c2 == c3;
c2 % c3 == 0;
那么存在整数x1
c2 == x1 * c3;
则 c1 = x2 * c2 + c3;
= (x1 * x2 + 1) * c3;
令(x1* x2 + 1) = k1;
c1 = k1 * c3;
b = (x3 * (x1 * x2 + 1) + x1) * c3;
令(x3* (x1* x2 + 1) + x1 = k2;
b = k2 * c3;
同样的 a = k3 * c3;
最后我们发现
a = k3 * c3;
b = k2 * c3;
所以c3一定是a和b的公因数(至于为什么是最大公因数,后面会讲);
将上述过程进行拓展和一般化即可知道:a和b的公因数==b和a%b的公因数
即 :gcd(a,b)==gcd(b,a%b)
int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
严格证明如下:
第一步:基础情况
当b = 0时,根据辗转相除法的定义,a就是最大公因数,即gcd(a, 0) = a。此时算法结束,结论成立。
第二步:归纳假设
假设对于任意正整数c和d,满足c >= d,并且辗转相除法能够正确地求出它们的最大公因数,即gcd(a, b) = gcd(b, a % b)。
第三步:归纳证明
我们需要证明对于a和b,辗转相除法能够正确地求出它们的最大公因数,即gcd(a, b) = gcd(b, a % b)。
根据辗转相除法的定义,我们有a = bq + r,其中q是商,r是余数。
假设d是a和b的一个公因数,即d能同时整除a和b。那么我们有以下两个等式:
- a = dx,其中x是一个整数。
- b = dy,其中y是一个整数。
我们需要证明d也是b和a % b的公因数。根据辗转相除法的定义,我们有a % b = a - (a / b) * b = a - q * b,其中a / b是整除运算的结果。
将上述等式代入第一个等式中,我们可以得到: a % b = dx - q * dy = d(x - qy)
由此可见,d也能整除a % b,即d是b和a % b的公因数。
反之,假设d是b和a % b的一个公因数,即d能同时整除b和a % b。那么我们有以下两个等式:
- b = dz,其中z是一个整数。
- a % b = dw,其中w是一个整数。
我们需要证明d也是a和b的公因数。根据第一个等式,我们可以得到: a = a / b * b + a % b = (a / b) * dz + dw = d( (a / b) * z + w)
由此可见,d也能整除a,即d是a和b的公因数。
综上所述,d是a和b的公因数当且仅当d是b和a % b的公因数。
因此,根据归纳假设,辗转相除法能够正确地求出a和b的最大公因数,即gcd(a, b) = gcd(b, a % b)。
通过数学归纳法的证明,我们可以得出结论:辗转相除法能够正确地求出任意两个正整数的最大公因数。
拓展欧几里得几何
这里需要知道一个定理
裴蜀定理:
假设a和b是任意两个整数,一定存在整数x和y,使得满足以下等式:
ax + by = gcd(a, b)
由上我们讲过的a和b的公因数==b和a%b的公因数
可以知道
ax + by = gcd(a, b)=gcd(b, a % b)
也有
ax + (a % b)y = gcd(b, a % b)
设
d = gcd(b, a % b)=gcd(b, a % b);
所以
ax + (a % b)y = d;
整理得
ax+b(y-(a/b)*x)=d
所以
ax + by=d;———————1式
ax+b(y-(a/b)*x)=d;————2式
1式与2式等价
这就是我们写 exgcd代码的依据
int exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int t =exgcd(b, a % b, y, x);
y = y - a / b * x;
return t;
}