今天搞明白了 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,c∈N (a+b≠0a + b\neq0a+b̸=0)且已知,p,q∈Zp, q \in \mathbb{Z}p,q∈Z 且未知,求$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=p0∗c/gcd(a,b)
④ q1=q0∗c/gcd(a,b)q_1 = q_0 * c / gcd(a, b)q1=q0∗c/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=q1−a/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,q0 与 p1,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})(pn−i,qn−i) 。这就是 exgcdexgcdexgcd 的思路。
三. 关键步骤推导
考虑以下式子:
⑩ p0∗a+q0∗b=gcd(a,b)p_0 * a + q_0 * b = gcd(a, b)p0∗a+q0∗b=gcd(a,b)
p1∗b+q1∗(a%b)=gcd(b,a%b)\ \ \ \ p_1 * b + q_1 * (a\%b) = gcd(b, a\%b) p1∗b+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)p0∗a+q0∗b=p1∗b+q1∗(a%b)
⇒\Rightarrow⇒ 考虑等式右边,提取 a,ba, ba,b:
⑫ p1∗b+q1∗(a%b)p_1 * b + q_1 * (a\%b)p1∗b+q1∗(a%b)
=p1∗b+q1∗(a−a/b∗b)= p_1 * b + q_1 * (a - a / b * b)=p1∗b+q1∗(a−a/b∗b)
=p1∗b+q1∗a−(q1∗a/b∗b)= p1 * b + q_1 * a - (q_1 * a / b * b)=p1∗b+q1∗a−(q1∗a/b∗b)
=q1∗a+(p1−q1∗a/b)∗b= q_1 * a + (p_1 - q_1 * a / b) * b=q1∗a+(p1−q1∗a/b)∗b
于是我们可以将等式右边转化为一个与 a,ba, ba,b 有关的式子。观察可发现此过程中 a,ba,ba,b 保持不变,变化的是前后的 p,qp, qp,q。
⇒\Rightarrow⇒ 将 ⑪ ⑫ 结合,可得 p0,q0p_0, q_0p0,q0 与 p1,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=p1−q1∗a/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=p2−q2∗(2/1)=1 (a,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=p1−q1∗(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));
//此处一定要注意是先除后乘 不要搞反顺序
}
本文详细介绍了扩展欧几里得算法(exGCD)的基本概念、求解思路及关键步骤,并通过实例加以说明,最后给出了C++实现代码。
7526

被折叠的 条评论
为什么被折叠?



