gcd(a,b) 表示 整数 a 与 b 的最大公约数.
若 t=amodb, 则 gcd(a,b)=gcd(b,t)
证明略.
gcd 接受变量元组 (a,b) 作为输入,输出最大公约数 (r).
我们很难直接计算出 r,但可以通过一些中间步骤逐步计算得到 r.
我们定义一个中间状态
(c,d,t)其中 t=cmodd
定义一个状态转移过程,将一个状态映射到下一个状态.
cdt⇒dtdmodt将初始状态赋值为 (a,b,amodb),则 r=gcd(a,b)
进行状态转移
abamodb⇒bamodbbmod(amodb)此时
r=gcd(b,amodb)=gcd(a,b)由归纳法可得,在任意一个中间状态 (c,d,t) 时,有
r=gcd(c,d)状态转移不能无限进行下去,要有一个终止条件,即 t=0.
当 t=cmodd=0 时,显然 d∣c, r=gcd(c,d)=d.
在最终状态 (c,d,0) 时, d 就是最大公约数 r.
int gcd(const int a, const int b) {
int c = a, d = b, t = a % b;
while (t) {
c = d;
d = t;
t = c % d;
}
return d;
}
实际上,可以减少变量个数,直接使用输入时定义的变量 a, b 存放中间状态,即我们通常所见的 gcd 实现。
设
r=gcd(a,b)=a⋅x+b⋅y则扩展欧几里得算法可以在求出 r 的同时,得出二元一次方程r=a⋅x+b⋅y 的一组整数解。
exgcd 是一个映射
(a,b)⇒(r,x,y)可以这样定义中间状态
(c,d,q,t,x,y)其中
qt=⌊dc⌋=cmodd=a⋅x+b⋅y继承 gcd 的状态转移过程
cdqtxy⇒dt⌊td⌋dmodtx′y′当 dmodt=0 时, r=gcd(c,d)=gcd(d,t)=t.
此时该状态保证 r=t=a⋅x+b⋅y, 即得解.
如何递推计算 x,y ?
tdmodt=a⋅x+b⋅y=a⋅x′+b⋅y′==dmodtd−⌊td⌋⋅td−⌊td⌋⋅(a⋅x+b⋅y)无法计算 x′,y′.
又注意到上式中的 d 阻断了递推,因此需要想办法用 x,y 表示 d.
这就是通常的 exgcd 递推算法.
定义状态
(c,d,q,t,x0,y0,x1,y1)其中
qtcd=⌊dc⌋=cmodd=a⋅x0+b⋅y0=a⋅x1+b⋅y1状态转移
cdqtx0y0x1y1⇒dt⌊td⌋dmodtx1y1x0−q⋅x1y0−q⋅y1证明: 状态转移不会破坏状态定义
===tc−q⋅d(a⋅x0+b⋅y0)−q⋅(a⋅x1+b⋅y1)(x0−q⋅x1)⋅a+(y0−q⋅y1)∗bdt=a⋅x1+b⋅y1=a⋅(x0−q⋅x1)+b⋅(y0−q⋅y1)因此转移后的状态仍满足定义。
初始状态
(a,b,⌊ba⌋,amodb,1,0,0,1)由归纳法可得,在任意一个中间状态,有 r=gcd(c,d)
当 t=cmodd=0 时, d∣c,r=gcd(c,d)=d.
r=d=a⋅x1+b⋅y1=a⋅x+b⋅y输出为 (d,x1,y1)
欧几里得算法: gcd.cpp
扩展欧几里得算法: exgcd.cpp