exgcd (扩展欧几里得算法)

首先下一个结论

exgcd是通过类似辗转相除的方法,用\(O(\log N)\) 的时间复杂度计算以下这个方程的一组解

\[ax + by = \gcd(a,b) \]

首先,我们知道\(\gcd(a,b) = \gcd(b,a\% b)\) , 也就是 \(\gcd(a,b) = \gcd(b,a-\lfloor \frac{a}{b}\rfloor \times b)\)

所以我们可以先往下递归,先算出下一层方程,也就是 $$bx + (a-\lfloor \frac{a}{b}\rfloor \times b) y = \gcd(a,b)$$

这个方程的解。

由于是像辗转相除一样一层一层递归的,所以最终总会有 b = 0 的时候,那么这时有一个很显然的结论 x = 1 , y = 0;

于是现在考虑另一个问题,我们已经得到了上面这个方程的一组解,如何得到原始方程的解呢?

设已知的那组解是 \(x_1\)\(y_1\) , 把式子拆开 $$bx_1 + ay_1 - \lfloor \frac{a}{b}\rfloor \times by_1 = \gcd(a,b)$$

再合并一下

\[ay_1 - b(x_1 - \lfloor \frac{a}{b}\rfloor \times y_1) = \gcd(a,b) \]

嗯!惊奇的发现 x 和 y 似乎可以用已知量表示了,即

\[x = y_1 \]

\[y = x_1 - \lfloor \frac{a}{b}\rfloor \times y_1 \]

那么一直递归就可以求出最终解了

参考代码

inline void exgcd(int a ,int b ,int &x  ,int &y) {
		if(!b) {
			x = 1 , y = 0; return;
		}
		int x1 , y1;
		exgcd(b , a - a / b * b , x1 , y1); //第二个传参图方便也可以写a%b
		y = x1 - a / b * y1; x = y1;
	}

这个时候我们可以进一步的扩展,求解方程$$ax + by = c$$

首先要知道一个东西,裴蜀定理

证明不重要,你只需要知道这个东西说明了如果在上面的方程里 \(gcd(a,b)\nmid c\) , 这个方程就是无整数解的。

那么其他情况下 \(gcd(a,b) \mid c\),只需在求出普通的解后 x 和 y 都乘以\(\frac{c}{\gcd(a,b)}\) 就好啦

大概就是这样了,可以做一下这道题洛谷P1082 同余方程,参考代码放这里了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b;

inline void exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0){x=1,y=0; return;}
	ll x0,y0;
	exgcd(b,a%b,x0,y0);
	x=y0,y=x0-y0*(a/b);
}
int main()
{
	cin>>a>>b; ll ans1,ans2;
	exgcd(a,b,ans1,ans2);
	cout<<(ans1+b)%b;
	return 0;
}
posted @ 2025-03-15 12:24  「癔症」  阅读(109)  评论(0)    收藏  举报