今天搞明白了
g
c
d
gcd
gcd 的思路,于是来发一篇博客,记录我的思路,也供大家参考。
前提:理解辗转相除法。
本文包含5个部分:
一. e x g c d exgcd exgcd 的作用
二. e x g c d exgcd exgcd 的求解思路
三. 关键步骤推导
四. 举例说明
五. C++代码实现
你可以选取感兴趣的部分进行月阅读。
注:
本文所用除法 "
/
/
/ " 皆为整除,舍弃余数。
本文在将两组逻辑关联起来时会使用
⇒
\Rightarrow
⇒ 进行强调。
一. e x g c d exgcd exgcd 的作用
e
x
g
c
d
exgcd
exgcd 是用来解决这样的问题的:
①
p
a
+
q
b
=
c
pa + qb = c
pa+qb=c
其中
a
,
b
,
c
∈
N
a, b, c \in \mathbb{N}
a,b,c∈N (
a
+
b
≠
0
a + b\neq0
a+b̸=0)且已知,
p
,
q
∈
Z
p, q \in \mathbb{Z}
p,q∈Z 且未知,求$p, q $ 。
⇒
\Rightarrow
⇒ 要解决这个问题,考虑下面这个式子:
②
p
a
+
q
b
=
g
c
d
(
a
,
b
)
pa + qb = gcd(a, b)
pa+qb=gcd(a,b)
我们需要从中得出一组解
(
p
0
,
q
0
)
(p_0, q_0)
(p0,q0) ,然后可得 ① 的一组解
(
p
1
,
q
1
)
(p_1, q_1)
(p1,q1) 满足:
③
p
1
=
p
0
∗
c
/
g
c
d
(
a
,
b
)
p_1 = p_0 * c / gcd(a, b)
p1=p0∗c/gcd(a,b)
④
q
1
=
q
0
∗
c
/
g
c
d
(
a
,
b
)
q_1 = q_0 * c / gcd(a, b)
q1=q0∗c/gcd(a,b)
⇒
\Rightarrow
⇒ 而 ① 的其他解满足:
⑤
p
=
p
1
+
b
/
g
c
d
(
a
,
b
)
∗
t
p = p_1 + b / gcd(a, b) * t
p=p1+b/gcd(a,b)∗t
⑥
q
=
q
1
−
a
/
g
c
d
(
a
,
b
)
∗
t
q = q_1 - a / gcd(a, b) * t
q=q1−a/gcd(a,b)∗t
其中
t
t
t 是任意整数,不同的
t
t
t 对应不同的解。
详细内容请查阅相关资料,本文将不在此处展开。
二. e x g c d exgcd exgcd 的求解思路
那么问题来了,如何求解
②
p
a
+
q
b
=
g
c
d
(
a
,
b
)
pa + qb = gcd(a, b)
pa+qb=gcd(a,b) 呢?
⇒
\Rightarrow
⇒ 我们知道当
b
=
0
b = 0
b=0 时:
⑦
g
c
d
(
a
,
b
)
=
g
c
d
(
a
,
0
)
=
a
gcd(a, b) = gcd(a, 0) = a
gcd(a,b)=gcd(a,0)=a。
可知此时:
⑧
p
a
+
q
b
=
g
c
d
(
a
,
b
)
pa + qb = gcd(a, b)
pa+qb=gcd(a,b)
p
a
=
a
\ \ \ \ pa = a
pa=a
于是我们可以取一组解
(
p
=
1
,
q
=
0
)
(p = 1, q = 0)
(p=1,q=0) 作为我们的突破口。
⇒
\Rightarrow
⇒ 我们还知道当
b
≠
0
b\neq0
b̸=0 时:
⑨
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)
考虑等式两边和 ② 的对应关系,发现两边对应的
p
,
q
p,q
p,q 一般是不一样的。于是我们分别设
p
0
,
q
0
,
p
1
,
q
1
p_0, q_0, p_1, q_1
p0,q0,p1,q1 来对应。
⇒ \Rightarrow ⇒ 然后我们能够想到,如果找到 p 0 , q 0 p_0, q_0 p0,q0 与 p 1 , q 1 p_1, q_1 p1,q1 之间的对应关系,就能够通过反复应用 ⑨ 减小 a , b a, b a,b 的值,在 b = 0 b = 0 b=0 时得到一组解 ( p n , q n ) (p_n, q_n) (pn,qn) 之后,反推找到之前每个步骤的 ( p n − i , q n − i ) (p_{n-i}, q_{n-i}) (pn−i,qn−i) 。这就是 e x g c d exgcd exgcd 的思路。
三. 关键步骤推导
考虑以下式子:
⑩
p
0
∗
a
+
q
0
∗
b
=
g
c
d
(
a
,
b
)
p_0 * a + q_0 * b = gcd(a, b)
p0∗a+q0∗b=gcd(a,b)
p
1
∗
b
+
q
1
∗
(
a
%
b
)
=
g
c
d
(
b
,
a
%
b
)
\ \ \ \ p_1 * b + q_1 * (a\%b) = gcd(b, a\%b)
p1∗b+q1∗(a%b)=gcd(b,a%b)
通过 ⑨ 我们可以得到:
⑪
p
0
∗
a
+
q
0
∗
b
=
p
1
∗
b
+
q
1
∗
(
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
,
b
a, b
a,b:
⑫
p
1
∗
b
+
q
1
∗
(
a
%
b
)
p_1 * b + q_1 * (a\%b)
p1∗b+q1∗(a%b)
=
p
1
∗
b
+
q
1
∗
(
a
−
a
/
b
∗
b
)
= p_1 * b + q_1 * (a - a / b * b)
=p1∗b+q1∗(a−a/b∗b)
=
p
1
∗
b
+
q
1
∗
a
−
(
q
1
∗
a
/
b
∗
b
)
= p1 * b + q_1 * a - (q_1 * a / b * b)
=p1∗b+q1∗a−(q1∗a/b∗b)
=
q
1
∗
a
+
(
p
1
−
q
1
∗
a
/
b
)
∗
b
= q_1 * a + (p_1 - q_1 * a / b) * b
=q1∗a+(p1−q1∗a/b)∗b
于是我们可以将等式右边转化为一个与
a
,
b
a, b
a,b 有关的式子。观察可发现此过程中
a
,
b
a,b
a,b 保持不变,变化的是前后的
p
,
q
p, q
p,q。
⇒
\Rightarrow
⇒ 将 ⑪ ⑫ 结合,可得
p
0
,
q
0
p_0, q_0
p0,q0 与
p
1
,
q
1
p_1, q_1
p1,q1 之间关系为:
⑬
p
0
=
q
1
p_0 = q_1
p0=q1
q
0
=
p
1
−
q
1
∗
a
/
b
\ \ \ \ \ q_0 = p_1 - q_1 * a / b
q0=p1−q1∗a/b
四. 举例说明
好的,大致上我们的问题就解决了。来举个粒例子吧:
求 3 p + 2 q = g c d ( 3 , 2 ) 3p + 2q = gcd(3, 2) 3p+2q=gcd(3,2) 的一组解:
3
p
0
+
q
0
=
g
c
d
(
3
,
2
)
3p_0 + q_0 = gcd(3, 2)
3p0+q0=gcd(3,2)
2
p
1
+
q
1
=
g
c
d
(
2
,
1
)
2p_1 + q_1 = gcd(2, 1)
2p1+q1=gcd(2,1)
p
2
+
0
=
g
c
d
(
1
,
0
)
p_2 + 0 = gcd(1, 0)
p2+0=gcd(1,0)
p
2
=
1
,
q
2
=
0
p_2 = 1,\ \ \ \ \ \ \ \ \ \ \ q_2 = 0
p2=1, q2=0
p
1
=
q
2
=
0
,
q
1
=
p
2
−
q
2
∗
(
2
/
1
)
=
1
p_1 = q_2 = 0,\ \ q_1 = p_2 - q_2 * (2 / 1) = 1
p1=q2=0, q1=p2−q2∗(2/1)=1 (
a
,
b
a,b
a,b 应对应
p
1
,
q
1
p_1, q_1
p1,q1,即下标较小那项)
p
0
=
q
1
=
1
,
q
0
=
p
1
−
q
1
∗
(
3
/
2
)
=
−
1
p_0 = q_1 = 1,\ \ q_0 = p_1 - q_1 * (3 / 2) = -1
p0=q1=1, q0=p1−q1∗(3/2)=−1 (对应
p
0
,
q
0
p_0, q_0
p0,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));
//此处一定要注意是先除后乘 不要搞反顺序
}