数学/数论专题-学习笔记:扩展欧几里得(exgcd)
1. 前言
扩展欧几里得(exgcd),是在欧几里得算法基础上求解任意形如 的二元一次方程的一组特解的一种算法。
在往下看之前,您只需要知道如何使用欧几里得算法求 。
不知道也没关系,式子在这里:
证明网络上面有很多,可以自行查找。
那么 就是其扩展算法。
2. 详解
2.1 exgcd 过程
例题 1:给出 ,求方程 的一组整数解。
这就是 要解决的问题。
接下来给出证明 一定有整数解的证明过程,该过程同时也给出了 的求解过程。
考虑普通 的求解过程:。
假设我们现在已经知道了一组解 是方程 的一组特解,那么如何求出 的解呢?
首先我们知道这个式子成立:
那么把上面这个式子带入到 中,就有:
拆掉括号:
左边转化一下:
上述方程跟下述方程是等价的:
两者对比,得到:
于是我们成功的通过 推出了 的解。
那么为什么一定会有解呢?
考虑欧几里得算法求 的步骤:。
仿照上述过程求 ,那么初始状态是 ,其中 。
因此 。
然后根据上述分析不断还原,最后一定能够还原出 的解。
证毕。
上述过程给出了 一定有解的证明,同时阐述了为什么有解。
同时根据上述过程应该明白为什么右边是 了吧~这跟 的求法有很大关系。
Code:
void exgcd(int a, int b, LL &x, LL &y)//注意 x 跟 y 是引用
{
if (b == 0) {x = 1; y = 0; return ;}
exgcd(b, a % b, x, y);
LL p = x; x = y; y = p - ((LL)a / b) * y;//特别注意存一下 x 的初始值
}
int main()
{
a = Read(), b = Read(); LL x, y;
exgcd(a, b, x, y); printf("%lld %lld\n", x, y);
}
2.2 一般情况
例题 2:P5656 【模板】二元一次不定方程 (exgcd)
这道题有一点恶心,主要是因为 0 不是正整数(
本题要求解决 的正整数解的若干问题:判定无整数解,有无正整数解,有正整数解的情况下正整数解中 的最小最大值。
首先根据裴蜀定理,得知如果 则原方程无整数解。
否则设我们用 exgcd 求出方程 的一组特解为 ,那么原方程的一组特解为 ,其实就是两边同乘 使得等式右边为 。
根据数学知识,在实数域上 的通解为:
但是要求 是整数,而为了覆盖所有符合要求的整数解则要求 尽量小,据此知道 可以使 尽量小,因此在整数域上 的通解为:
接下来考虑按照例题要求判断有无正整数解,显然如果 越小那么 越大,因此我们可以考虑求出 的最小正整数值,注意到通解形式就是不断往 上加上或减去 ,因此最小正整数值显然在 内,因此我们直接将 对 取模即可(特别注意 的情况要化成 ,后面的所有取模同理,不再赘述),然后用 算出 ,此时在正整数解内有 最小 最大,但若此时 依然不是正整数那么就是无正整数解的情况,仿照求 最小值求出 最小值即可。
剩下的情况就是有正整数解的情况了,在之前我们已经求出了 最小 最大的解,类似的求出 最大 最小的解就得到了 最小值最大值,至于解的个数,由于相邻两个正整数解 差值就是 ,因此解的个数就是 (不理解的考虑等差数列求项数)。
写代码的时候如果要写 对 取模可以这么写:x = (x % p + p) % p
,然后注意一下全部开 long long 以及 最小值不能有 0 即可。
Code:
/*
========= Plozia =========
Author:Plozia
Problem:P5656 【模板】二元一次不定方程 (exgcd)
Date:2022/10/10
========= Plozia =========
*/
#include <bits/stdc++.h>
typedef long long LL;
// const int MAXN = ;
LL a, b, c;
int Read()
{
int sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return sum * fh;
}
LL gcd(LL a, LL b) { return (b == 0) ? a : gcd(b, a % b); }
void exgcd(LL a, LL b, LL &x, LL &y)
{
if (b == 0) { x = 1, y = 0; return ; }
exgcd(b, a % b, x, y); LL p = x; x = y; y = p - (a / b) * y;
}
void Solve()
{
a = Read(), b = Read(), c = Read(); LL d = gcd(a, b), x, y;
if (c % d != 0) { puts("-1"); return ; }
exgcd(a, b, x, y); x *= c / d; y *= c / d;
x = (x % (b / d) + (b / d)) % (b / d); if (x == 0) x += b / d; y = (c - a * x) / b;
if (x > 0 && y <= 0) { printf("%lld %lld\n", x, (((y = (y % (a / d) + (a / d)) % (a / d)) == 0) ? (a / d) : y)); return ; }
LL Minx = x, Maxy = y; y = (y % (a / d) + (a / d)) % (a / d); if (y == 0) y += a / d; x = (c - b * y) / a;
LL Maxx = x, Miny = y; printf("%lld %lld %lld %lld %lld\n", (Maxx - Minx) / (b / d) + 1, Minx, Miny, Maxx, Maxy);
}
int main()
{
int t = Read(); while (t--) Solve(); return 0;
}
3. 总结
exgcd 求 的方式:类似求 ,若 则 。
exgcd 求 整数解的一般方式:
- 则无解。
- 求出 的一组特解 后,有 ,整数解 的通解如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具