欧几里得
本质:利用辗转相减法求最大公约数,即 gcd(a, b)。
数学表达: 设 a > b ,则
gcd(a, b) = gcd(a-b, b)
不断地利用大的数减去小的数,就能得到最大公约数。
证明:
设 a = k1 * g, b = k2 * g, g = gcd(a, b)
那么 gcd(k1, k2) = 1
设 a > b <=> k1 > k2 大数减小数
那么 a = (k1 - k2) * g, b = k2 * g
我们可以发现a,b的变化为两个互质的系数在不断相减(始终还是互质),
g始终是它们的最大公约数,所以当有一个数变为0时,另一个不为0的数就是最大公约数。
代码如下:
int getgcd (int a, int b)
{
if (a == 0)
return b;
else if (b == 0)
return a;
for (; a != b ;)
{
if (a > b)
a -= b;
else
b -= a;
}
}
---------------------------------------------------------------------------------------------------------------------------------
欧几里得优化(辗转相除)
我们发现当a一直大于b时,就会一直减去b,这其实就是变成 a mod b,于是我们可以利用辗转相除来优化。
代码如下:
int getgcd (int a,int b)
{
return b == 0 ? a : getgcd(b, a % b);
}
---------------------------------------------------------------------------------------------------------------------------------
扩展欧几里得
扩展欧几里得的典型应用就是解决形如a*x + b*y = c 的二元一次方程的解的存在性问题和求出特解与通解。
解的存在性问题:
我们通过辗转相减可以观察出,a和b辗转相减的时候相当于 a*x + b*y 中的x和y在不断变化的过程,我们通过前面可以知道
这个过程一定能够得到最大公约数,所以 a*x + b*y = gcd(a, b) 方程一定有解。
那么是否 gcd(a, b) ?= c 的时候就无解呢?首先我们知道 c 是gcd(a, b) 的倍数才可能有解,因为左边一定含有因子 gcd(a, b)
左边等于右边,那么右边也一定含有 gcd(a, b),因此我们可以通过两边同时除以最大公约数得到一个新的式子:
k1*x + k2*y = c / g && gcd(k1, k2) = 1
由辗转相减可得: k1*x + k2*y = 1 一定有解,所以方程 k1*x + k2*y = c / g 一定有解
由此可得, a*x + b*y = c 中c是gcd(a, b)的倍数时,方程一定有解
特解与通解:
所以我们只需要求解形如
a*x + b*y = gcd(a, b)
的方程,然后将解扩大 c / gcd(a, b) 倍就可以解出原方程的解
辗转相减可得:
(a - b)*x1 + b*y1 = gcd(a, b)
变换一下可得:
a*x1 + b*(y1 - x1) = gcd(a, b)
联立原方程得: x = x1, y = y1 - x1
我们可以不断将原问题变成一个规模更小的子问题,然后根据子问题的答案退出原问题的答案
又考虑可以用辗转相除优化,我们可以讲原问题变成求解:
b*x1 + a%b*y1 = gcd(a, b)
由
a%b = a - a/b * b
代入得
b*x1 + (a - a/b * b)*y1 = gcd(a, b)
得到
x = y1, y = x1 - a/b * y1
因此我们只需一直缩小问题规模,知道变成 gcd(a, b)*x + 0*y = gcd(a, b),
得到一组特解 (1, 0),再讲其反推回去,得到原方程的一组特解。这个过程可以用一个递归函数来实现。
代码如下:
int getextcgd (int a, int b, int &x, int &y) // a*x + b*y = gcd (a,b)
{
int d = a;
if (b != 0)
{
d = extgcd (b, a % b, x, y);
x -= (a / b) * y;
swap (x, y);
}
else
{
x = 1, y = 0;
}
return d;
}
函数执行完毕后x和y就是 a*x + b*y = gcd(a, b) 中的一组特解,
通解的变化规律就是 x 和 y 的值往相反的方向变化,比如 x 变大一些, y 变小一些,使得答案不变,
设这个变化的最小单位值为 d1, d2
a*(x + d1) + b*(y - d2) = gcd(a, b)
有
a * d1 = b * d2
两边同除 gcd(a, b) 令 k1 = a / gcd(a, b), k2 = b / gcd(a, b)
k1 * d1 = k2 * d2
这个时候k1,k2互质,显然 d1 = k2, d2 = k1,可得通解为
(x + k*d1, y - k*d2)
即
(x + k*b/gcd(a, b), y - k*a/gcd(a, b))
并且我们通过递归函数的实现可以观察到,解的绝对值是跟 a, b 的绝对值同一个级别的