扩展欧几里得算法(乘法逆元、不定方程求整数解)

欧几里得算法(辗转相除法)

也就是 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)

知乎上看到了一个比较好的证明(证明过程不重要):

我们只需要证明a,b的公因数集等于b,a%b的公因数集,那么这两个集合里的最大值肯定也相等,即 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)

a = b q + r a=bq+r a=bq+r,要证明:若d是a和b的公因数,当且仅当d是b和r的公因数

1)设d是a和b的公因数,则d|a且d|b,于是d|(a-bq),也就是d|r ==》d是b,r的公因数
2)设d是b和r的公因数,则d|b且d|r,于是d|(bq+r),也就是d|a ==》d是a,b的公因数
综上,a,b的所有公因数和b,r的所有公因数是一样的。那么,d是a和b的最大公因数,当且仅当d是b和r的最大公因数,即 g c d ( a , b ) = d = g c d ( b , a % b ) gcd(a,b)=d=gcd(b,a\%b) gcd(a,b)=d=gcd(b,a%b)

扩展欧几里得算法

这个算法说到底就是用来解决:求 a x + b y = c ax+by=c ax+by=c的整数解。

至于求乘法逆元之类的应用其实应该和算法本身是独立的,应该分开来讲。

先讲如何求解。

求解

首先根据裴蜀定理,这个方程有解,当且仅当 c % g c d ( a , b ) = 0 c\%gcd(a,b)=0 c%gcd(a,b)=0,或者说c是gcd(a,b)的整数倍。(裴蜀定理的证明放在后面,依然不重要)

不妨设 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),由欧几里得算法, g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)

因此 a x + b y = g c d ( a , b ) = g c d ( b , a % b ) = b x ′ + ( a % b ) y ′ ax+by=gcd(a,b)=gcd(b,a\%b)=bx'+(a\%b)y' ax+by=gcd(a,b)=gcd(b,a%b)=bx+(a%b)y

这里求x,y的解需要利用递推的思想。假设已知上一层x’和y‘,如何推导出当前x,y的值?这里需要稍微手算一下。

把a%b写成a-a/b*b,于是得到 a x + b y = b x ′ + ( a − a / b ∗ b ) y ′ = a y ′ + b ( x ′ − a / b ∗ y ′ ) ax+by=bx'+(a-a/b*b)y'=ay'+b(x'-a/b*y') ax+by=bx+(aa/bb)y=ay+b(xa/by)

通过计算得到 x = y ′ , y = x ′ − a / b ∗ y ′ x=y',y=x'-a/b*y' x=y,y=xa/by

ok,现在已经找到了由上一层解推出当前层解的递推关系了,那么递推的出口在哪里呢?

由于每递归一层,a和b的变化是:a=b,b=a%b,就相当于做了一层辗转相除法。所以最终,当a=gcd,b=0时,这个方程就变成了

g c d ∗ x + 0 ∗ y = g c d gcd*x+0*y=gcd gcdx+0y=gcd此时x=1,y=0就是一组特解(其实y也可以不为0,但是没必要,还可能导致爆int之类的)。找到解后就可以回溯,递推计算出每一层的x,y了。

于是顺便证明一下裴蜀定理(老证明癌了):

充分性:若 c % g c d ( a , b ) = 0 c\%gcd(a,b)=0 c%gcd(a,b)=0的倍数,那么总是可以通过上面的扩欧算法求出一组整数解。
必要性:若 a x + b y = c ax+by=c ax+by=c存在整数解 a x 1 + b y 1 = c ax_1+by_1=c ax1+by1=c (x1,y1为具体整数),设d是a,b的最大公因数即d|a,d|b,于是d|(ax1+by1),即d|c, c % g c d ( a , b ) = 0 c\%gcd(a,b)=0 c%gcd(a,b)=0
证毕

扩欧板子

非常简洁的两行代码

void Exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) x = 1, y = 0;
    else Exgcd(b, a % b, y, x), y -= a / b * x;
}

由于 a x + b y = a x + a b + b y − a b = a ( x + b ) + b ( y − a ) ax+by=ax+ab+by-ab=a(x+b)+b(y-a) ax+by=ax+ab+byab=a(x+b)+b(ya),也就是 x ′ = x + b , y ′ = y − a x'=x+b,y'=y-a x=x+b,y=ya同样是方程的解
所以有时对x或y取值范围有要求(比如要求为正整数)就可以通过同时修改x和y来进行调整,比如说

Exgcd(a,b,x,y);
while(x<0) x+=b,y-=a;

或者直接写成

x = (x % b + b) % b;

应用

把扩欧算法讲清楚之后,具体怎么应用其实就要自己判断了。比如说比较常见的的通过扩欧求乘法逆元

求a在mod b意义下的逆元,即求 a x ≡ 1 ( m o d   b ) ax\equiv1(mod\ b) ax1(mod b)

把这个式子化一下,其实就变成求 a x + b y = 1 ax+by=1 ax+by=1中x的整数解的问题

根据裴蜀定理,这个不定方程有整数解的前提是gcd(a,b)=1,即a,b互质。(而保证互质的一个方法是令a或b为一个大质数,比如998244353或者1e9+7之类的,所以如果题目莫名给了一个大质数或者a,b互质的条件,很可能就是在保证扩欧方程有解的前提)

把a,b带入方程之后算出x,并调整x的范围即可。

void Exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) x = 1, y = 0;
    else Exgcd(b, a % b, y, x), y -= a / b * x;
}
ll x, y;
Exgcd (a, p, x, y);
x = (x % p + p) % p;
printf ("%d\n", x); //x是a在mod p下的逆元

Fraction Construction Problem
有时题目要求计算 a x − b y = c ax-by=c axby=c,这时候只需要转化成 a x + b ( − y ) = c ax+b(-y)=c ax+b(y)=c,最后y取相反数就可以了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
扩展欧几里得算法是求一元一次不定方程 ax + by = gcd(a,b) 的一种方法,其中 a 和 b 是整数,gcd(a,b) 是它们的最大公约数,x 和 y 是整数。逆元是指在模运算下,一个数的乘法逆元是指与它相乘后模运算得到 1 的数。在数论中,常常需要求一个数在模意义下的逆元,即一个数 k 满足 (k * x) % m = 1,其中 m 是模数。 下面是扩展欧几里得算法求逆元的 C 语言实现: ```c #include <stdio.h> // 扩展欧几里得算法 int exgcd(int a, int b, int *x, int *y) { if (b == 0) { *x = 1; *y = 0; return a; } int gcd = exgcd(b, a % b, y, x); *y -= a / b * (*x); return gcd; } // 求逆元 int modinv(int a, int m) { int x, y; int gcd = exgcd(a, m, &x, &y); if (gcd != 1) { return -1; // a 和 m 不互质,不存在逆元 } else { return (x % m + m) % m; // 转化为正整数 } } int main() { int a = 3, m = 11; int inv = modinv(a, m); if (inv == -1) { printf("%d 在模 %d 意义下不存在逆元\n", a, m); } else { printf("%d 在模 %d 意义下的逆元是 %d\n", a, m, inv); } return 0; } ``` 这个程序中,exgcd 函数通过递归实现扩展欧几里得算法,返回 a 和 b 的最大公约数,并且求出 x 和 y 的值。在 modinv 函数中,我们调用 exgcd 函数求出 a 和 m 的最大公约数,并且判断 a 和 m 是否互质,如果不互质则不存在逆元。否则,根据扩展欧几里得算法的结果,求出 x 的值作为 a 在模 m 意义下的逆元。注意,由于 x 可能是负数,所以要将其转化为正整数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值