算法之美 - 如何清晰理解扩展欧几里得(exgcd)

本文详细介绍了扩展欧几里得算法(exGCD)的基本概念、求解思路及关键步骤,并通过实例加以说明,最后给出了C++实现代码。

今天搞明白了 gcdgcdgcd 的思路,于是来发一篇博客,记录我的思路,也供大家参考。
前提:理解辗转相除法。

本文包含5个部分:

一. exgcdexgcdexgcd 的作用
二. exgcdexgcdexgcd 的求解思路
三. 关键步骤推导
四. 举例说明
五. C++代码实现

你可以选取感兴趣的部分进行阅读。

注:
本文所用除法 " /// " 皆为整除,舍弃余数。
本文在将两组逻辑关联起来时会使用 ⇒\Rightarrow 进行强调。


一. exgcdexgcdexgcd 的作用

exgcdexgcdexgcd 是用来解决这样的问题的:
pa+qb=cpa + qb = cpa+qb=c
其中 a,b,c∈Na, b, c \in \mathbb{N}a,b,cNa+b≠0a + b\neq0a+b̸=0)且已知,p,q∈Zp, q \in \mathbb{Z}p,qZ 且未知,求$p, q $ 。

⇒\Rightarrow 要解决这个问题,考虑下面这个式子:
pa+qb=gcd(a,b)pa + qb = gcd(a, b)pa+qb=gcd(a,b)
我们需要从中得出一组解 (p0,q0)(p_0, q_0)(p0,q0) ,然后可得 ① 的一组解 (p1,q1)(p_1, q_1)(p1,q1) 满足:
p1=p0∗c/gcd(a,b)p_1 = p_0 * c / gcd(a, b)p1=p0c/gcd(a,b)
q1=q0∗c/gcd(a,b)q_1 = q_0 * c / gcd(a, b)q1=q0c/gcd(a,b)

⇒\Rightarrow 而 ① 的其他解满足:
p=p1+b/gcd(a,b)∗tp = p_1 + b / gcd(a, b) * tp=p1+b/gcd(a,b)t
q=q1−a/gcd(a,b)∗tq = q_1 - a / gcd(a, b) * tq=q1a/gcd(a,b)t

其中 ttt 是任意整数,不同的 ttt 对应不同的解。
详细内容请查阅相关资料,本文将不在此处展开。


二. exgcdexgcdexgcd 的求解思路

那么问题来了,如何求解
pa+qb=gcd(a,b)pa + qb = gcd(a, b)pa+qb=gcd(a,b) 呢?

⇒\Rightarrow 我们知道当 b=0b = 0b=0 时:
gcd(a,b)=gcd(a,0)=agcd(a, b) = gcd(a, 0) = agcd(a,b)=gcd(a,0)=a
可知此时:
pa+qb=gcd(a,b)pa + qb = gcd(a, b)pa+qb=gcd(a,b)
    pa=a\ \ \ \ pa = a    pa=a
于是我们可以取一组解 (p=1,q=0)(p = 1, q = 0)(p=1,q=0) 作为我们的突破口。

⇒\Rightarrow 我们还知道当 b≠0b\neq0b̸=0 时:
gcd(a,b)=gcd(b,a%b)gcd(a, b) = gcd(b, a\%b)gcd(a,b)=gcd(b,a%b)
考虑等式两边和 ② 的对应关系,发现两边对应的 p,qp,qp,q 一般是不一样的。于是我们分别设 p0,q0,p1,q1p_0, q_0, p_1, q_1p0,q0,p1,q1 来对应。

⇒\Rightarrow 然后我们能够想到,如果找到 p0,q0p_0, q_0p0,q0p1,q1p_1, q_1p1,q1 之间的对应关系,就能够通过反复应用 ⑨ 减小 a,ba, ba,b 的值,在 b=0b = 0b=0 时得到一组解 (pn,qn)(p_n, q_n)(pn,qn) 之后,反推找到之前每个步骤的 (pn−i,qn−i)(p_{n-i}, q_{n-i})(pni,qni) 。这就是 exgcdexgcdexgcd 的思路。


三. 关键步骤推导

考虑以下式子:
p0∗a+q0∗b=gcd(a,b)p_0 * a + q_0 * b = gcd(a, b)p0a+q0b=gcd(a,b)
    p1∗b+q1∗(a%b)=gcd(b,a%b)\ \ \ \ p_1 * b + q_1 * (a\%b) = gcd(b, a\%b)    p1b+q1(a%b)=gcd(b,a%b)
通过 ⑨ 我们可以得到:
p0∗a+q0∗b=p1∗b+q1∗(a%b)p_0 * a + q_0 * b = p_1 * b + q_1 * (a\%b)p0a+q0b=p1b+q1(a%b)

⇒\Rightarrow 考虑等式右边,提取 a,ba, ba,b
p1∗b+q1∗(a%b)p_1 * b + q_1 * (a\%b)p1b+q1(a%b)
=p1∗b+q1∗(a−a/b∗b)= p_1 * b + q_1 * (a - a / b * b)=p1b+q1(aa/bb)
=p1∗b+q1∗a−(q1∗a/b∗b)= p1 * b + q_1 * a - (q_1 * a / b * b)=p1b+q1a(q1a/bb)
=q1∗a+(p1−q1∗a/b)∗b= q_1 * a + (p_1 - q_1 * a / b) * b=q1a+(p1q1a/b)b
于是我们可以将等式右边转化为一个与 a,ba, ba,b 有关的式子。观察可发现此过程中 a,ba,ba,b 保持不变,变化的是前后的 p,qp, qp,q

⇒\Rightarrow 将 ⑪ ⑫ 结合,可得 p0,q0p_0, q_0p0,q0p1,q1p_1, q_1p1,q1 之间关系为:
p0=q1p_0 = q_1p0=q1
     q0=p1−q1∗a/b\ \ \ \ \ q_0 = p_1 - q_1 * a / b     q0=p1q1a/b


四. 举例说明

好的,大致上我们的问题就解决了。来举个例子吧:

3p+2q=gcd(3,2)3p + 2q = gcd(3, 2)3p+2q=gcd(3,2) 的一组解:

3p0+q0=gcd(3,2)3p_0 + q_0 = gcd(3, 2)3p0+q0=gcd(3,2)
2p1+q1=gcd(2,1)2p_1 + q_1 = gcd(2, 1)2p1+q1=gcd(2,1)
p2+0=gcd(1,0)p_2 + 0 = gcd(1, 0)p2+0=gcd(1,0)

p2=1,           q2=0p_2 = 1,\ \ \ \ \ \ \ \ \ \ \ q_2 = 0p2=1,           q2=0
p1=q2=0,  q1=p2−q2∗(2/1)=1p_1 = q_2 = 0,\ \ q_1 = p_2 - q_2 * (2 / 1) = 1p1=q2=0,  q1=p2q2(2/1)=1a,ba,ba,b 应对应 p1,q1p_1, q_1p1,q1,即下标较小那项)
p0=q1=1,  q0=p1−q1∗(3/2)=−1p_0 = q_1 = 1,\ \ q_0 = p_1 - q_1 * (3 / 2) = -1p0=q1=1,  q0=p1q1(3/2)=1 (对应 p0,q0p_0, q_0p0,q0

得解为 (p=1,q=−1)(p = 1, q = -1)(p=1,q=1),代入式子成立。


五. C++代码实现
pair<int, int> exgcd(int a, int b) {
	//经过修改,不返回gcd(a, b),仅返回p, q值
    if(b == 0)
        return make_pair(1, 0);
    pair<int, int> sol = exgcd(b, a % b);
    int p = sol.first, q = sol.second;
    return make_pair(q, p - q * (a / b));
    //此处一定要注意是先除后乘 不要搞反顺序
}

扩展欧几里得算法是求解一元一次不定方程 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、付费专栏及课程。

余额充值