【笔记】 欧几里得(扩展欧几里得)

欧几里得

本质:利用辗转相减法求最大公约数,即 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 的绝对值同一个级别的

猜你喜欢

转载自blog.csdn.net/El_Apocalipsis/article/details/80996587