![2c9237adcfda9b88c73bb8e9c56b9775.png](https://i-blog.csdnimg.cn/blog_migrate/8834f9ae9e3dcc2f82b5b850b0fde91d.jpeg)
欧几里得算法
若 t = a % b, 则
证明略.
递推版 gcd 算法
gcd 接受变量元组 (a, b) 作为输入,输出最大公约数 (r).
我们很难直接计算出 r,但可以通过一些中间步骤逐步计算得到 r.
我们定义一个中间状态
定义一个状态转移过程,将一个状态映射到下一个状态.
将初始状态赋值为
进行状态转移
此时
由归纳法可得,在任意一个中间状态
状态转移不能无限进行下去,要有一个终止条件,即 t = 0.
当
在最终状态
代码
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 实现。
扩展欧几里得算法
设
递推版 exgcd 算法
exgcd 是一个映射
可以这样定义中间状态
其中
继承 gcd 的状态转移过程
当
此时该状态保证
如何递推计算 x, y ?
无法计算
又注意到上式中的 d 阻断了递推,因此需要想办法用 x, y 表示 d.
这就是通常的 exgcd 递推算法.
定义状态
其中
状态转移
证明: 状态转移不会破坏状态定义
因此转移后的状态仍满足定义。
初始状态
由归纳法可得,在任意一个中间状态,有
当
输出为
代码
typedef long long ll;
ll exgcd(const ll a, const ll b, ll &x, ll &y) {
ll c, d, q, t, x0, y0, x1, y1, temp;
c = a, d = b, q = a / b, t = a % b, x0 = 1, y0 = 0, x1 = 0, y1 = 1;
while (t) {
temp = x0, x0 = x1, x1 = temp - q * x1;
temp = y0, y0 = y1, y1 = temp - q * y1;
c = d;
d = t;
q = c / d;
t = c % d;
}
x = x1, y = y1;
return d;
}