作者按:写这篇随笔是为了解释 tourist 的逆元模板
template <typename T> T inverse(T a, T m) { T u = 0, v = 1; while (a != 0) { T t = m / a; m -= t * a; swap(a, m); u -= t * v; swap(u, v); } assert(m == 1); return u; // 注意:u 可能为负数 }
一般的扩展欧几里德算法是求 $x, y$ 使得 $ax + by = \gcd(a,b)$ 。若 $a$ 和 $m$ 互素,扩展欧几里得算法可以用来求 $a$ 在模 $m$ 下的逆元,常见的实现如下
int extgcd(int a, int b, int& x, int& y) { int d = a; if (b != 0) { d = extgcd(b, a % b, y, x); y -= (a / b) * x; } else { x = 1; y = 0; } return d; } int mod_inverse(int a, int m) { int x, int y; extgcd(a, m, x, y); return (m + x % m) % m; }
求 $a$ 在模 $m$ 下的逆元时,往往事先已经知道 $\gcd(a, m) = 1$;此外,对于方程 $ax + my = 1$,我们并不需要把 $x, y$ 都求出来,而只需要求出 $x$,也就是说我们不关心 $y$ 是多少。相比于 tourist 的实现,上述代码是不够高效的,一则含有冗余的计算,二则采用递归实现。下面分析 tourist 的非递归实现之原理。
$r_0 = r_1 q_2 + r_ 2 $
$r_1 = r_2 q_3 + r_3$
$r_2 = r_3 q_4 + r_4$
$r_{i - 2} = r_{i - 1} q_i + r_i $
$r_{n - 2} = r_{n - 1} q_n + r_{n}$
$r_{n} = 0$
我们有
$\gcd(r_0, r_1) = \gcd(r_1, r_2) = \dots = \gcd(r_{n - 2}, r_{n - 1}) = \gcd(r_{n - 1}, r_{n}) = r_{n - 1}$
令 $r_i = s_i a + t_i b$,
又 $r_i = r_{i - 2} - r_{i - 1} q_i $
故有
\begin{aligned}
r_i &= s_{i - 2} a + t_{i - 2} b - (s_{i - 1} a + t_{i - 1} b) q_i \\
&= (s_{i - 2} - q_i s_{i - 1}) a + (t_{i - 2} - t_{i - 1} q_i) b
\end{aligned}
注意到 $s_i$ 的递推式中未出现 $t$,而我们只要求出 $s_{n - 1}$ 即可,因此可以不管 $t$,只维护 $s$ 。
令 $r_0 = a, r_1 = m$,即 $s_0 = 1, s_1 = 0$;且令 $q_2 = 0$,即令 $r_2 = a$ 。
每次循环结束之后,最新的 $r$ 保存在变量 a
中。在循环开始之前最新的 $r$ 是 $r_2$ 。
通过上述分析可以知道当循环结束时,即 a
的值变为 $r_n = 0$ 时,m
的值是 $r_{n - 1} = \gcd(a, m)$ 。
我们看到两种实现所依据的都是欧几里得算法,下面来讨论扩展欧几里得算法算出的 $ax + by = \gcd(a,b)$ 的解的大小。
用归纳法可以证明,若 $ab\ne 0$,则 $|x| \le b$ 且 $|y|\le a$ 。
在 $ b = 0$ 的前一步,即 $a \% b = 0$ 时有 $x = 0$ 且 $y = 1$,结论显然成立。假设调用
extgcd(b, a % b, y', x')
后有 $|x'| \le b$ 且 $|y'| \le a \% b$ 。在extgcd(a, b, x, y)
中 $x = x', y = y' - (a / b) x '$,所以有如下不等式成立
$|x| = |x'| \le b, |y| = |y' - (a / b)x'| \le |y'| + (a /b )|x'| \le a \% b + (a / b) \times b = a$