今天搞明白了 g c d gcd gcd 的思路,于是来发一篇博客,记录我的思路,也供大家参考。
前提:理解辗转相除法。
本文包含5个部分:
你可以选取感兴趣的部分进行月阅读。
注:
本文所用除法 " / / / " 皆为整除,舍弃余数。
本文在将两组逻辑关联起来时会使用 ⇒ \Rightarrow ⇒ 进行强调。
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 对应不同的解。
详细内容请查阅相关资料,本文将不在此处展开。
那么问题来了,如何求解
② 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),代入式子成立。
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));
//此处一定要注意是先除后乘 不要搞反顺序
}