算法学习笔记(37)——扩展欧几里得算法
扩展欧几里得算法
欧几里得算法/辗转相除法(Euclidean algorithm)
辗转相除法,又称欧几里得算法(英语:Euclidean algorithm),是求最大公约数的算法。
两个整数的最大公约数是能够同时整除它们的最大的正整数。辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。
例如,252和105的最大公约数是21
;
因 ,所以147和105的最大公约数也是21。
在这过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至余为零。
注意:0与任何数的最大公约数即为该数。
这时,所剩下的还没有变成零的数就是两数的最大公约数。
裴蜀定理(Bézout 定理)
两数的最大公约数可以用两数的整数倍相加来表示,如
对任意整数 ,存在一对整数 ,满足
证明:
在欧几里得算法的最后一步,即 时,显然有一对整数 ,使得 。
若 ,则 ;
假设存在一对整数 ,满足 ;
由于 ;
则令 ;
即得到;
对欧几里得算法的递归过程应用数学归纳法,可知裴蜀定理成立。
证毕
扩展欧几里得算法(Extended Euclidean algorithm)
扩展欧几里得算法(英语:Extended Euclidean algorithm)是欧几里得算法(又叫辗转相除法)的扩展。已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式
假设 ,当求出一组x与y的值之后,即可求出所有的x与y的值:
故所有的x与y的值可以用已经求出的一组解 表示为:
// 求a与b的最大公约数,并求出满足裴蜀定理 ax+by=gcd(a,b) 的一组x与y的值
int exgcd(int a, int b, int &x, int &y)
{
/*
* 确定边界条件,如果b==0,则gcd(a,b)=a,则a*1+b*0=a一定满足裴蜀定理
* 这种边界情况在代码中是作为递归的最底层出现的
* 这意味着当这一层解出来的时候,我们需要将其返回到上一层的解,直到返回至最开始的一层。
*/
if (b == 0) { x = 1, y = 0; return a; }
/*
* 如果 b>0, gcd(a, b) = gcd(b, a%b)
* b*x + (a % b)y = b*x + (a - a / b * b)*y = gcd(a, b)
* a*y + b(x - a / b * y) = gcd(a, b)
* x' = y, y' = x - a/b*y
*/
int d = exgcd(b, a % b, x, y);
int z = x, y = x, x = z - a / b * y;
return d;
}
精简版:
int exgcd(int a, int b, int &x, int &y)
{
if (b == 0) { x = 1, y = 0; return a; }
// 在计算 b 与 a%b 的最大公约数时交换 x 与 y 的值,方便后续计算
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
if (b == 0) { x = 1, y = 0; return a; }
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- ) {
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
}
return 0;
}
求解线性同余方程
给定整数 ,求一个整数 满足 ,或者给出无解。因为未知数的指数为 ,所以我们称之为一次同余方程,也称线性同余方程。
等价于 是 的倍数,不妨设为 倍。则 ,于是,该方程可以改写为 。
则该问题转化为,给定 ,求出一组x与y的值,使得 成立。
而裴蜀定理则为,非定 ,求出一组x与y的值,使得 成立
根据裴蜀定理及其证明过程,线性同余方程有解当且仅当 。
所以求解过程如下,记 :
- 求出 的一组特解 (扩展欧几里得算法)
- 由于 , 则令 同时乘以 ,就得到了 的一组特解
事实上, 的通解可以表示为:
其中 $ k \in \mathbb{Z}d = gcd(a, m)x_0, y_0$ 是 的一组特解。
#include <iostream>
using namespace std;
typedef long long LL;
// 扩展欧几里得算法
int exgcd(int a, int b, int &x, int &y)
{
if (b == 0) { x = 1, y = 0; return a; }
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- ) {
int a, b, m;
scanf("%d%d%d", &a, &b, &m);
int x, y;
int d = exgcd(a, m, x, y);
// 线性同余方程有解的充要条件:b是d的倍数
if (b % d == 0) printf("%d\n", (LL)b / d * x % m);
else puts("impossible");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】