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

今天搞明白了 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,cN a + b ≠ 0 a + b\neq0 a+b̸=0)且已知, p , q ∈ Z p, q \in \mathbb{Z} p,qZ 且未知,求$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=p0c/gcd(a,b)
q 1 = q 0 ∗ c / g c d ( a , b ) q_1 = q_0 * c / gcd(a, b) q1=q0c/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=q1a/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}) (pni,qni) 。这就是 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) p0a+q0b=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)     p1b+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) p0a+q0b=p1b+q1(a%b)

⇒ \Rightarrow 考虑等式右边,提取 a , b a, b a,b
p 1 ∗ b + q 1 ∗ ( a % b ) p_1 * b + q_1 * (a\%b) p1b+q1(a%b)
= p 1 ∗ b + q 1 ∗ ( a − a / b ∗ b ) = p_1 * b + q_1 * (a - a / b * b) =p1b+q1(aa/bb)
= p 1 ∗ b + q 1 ∗ a − ( q 1 ∗ a / b ∗ b ) = p1 * b + q_1 * a - (q_1 * a / b * b) =p1b+q1a(q1a/bb)
= q 1 ∗ a + ( p 1 − q 1 ∗ a / b ) ∗ b = q_1 * a + (p_1 - q_1 * a / b) * b =q1a+(p1q1a/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=p1q1a/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=p2q2(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=p1q1(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));
    //此处一定要注意是先除后乘 不要搞反顺序
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值