# 扩展欧几里得算法 扩展欧几里得算法(Extended Euclidean algorithm, EXGCD),常用于求 $ax+by=\gcd(a,b)$ 的一组可行解。 --- ### 过程 设$ax_1+by_1=\gcd(a,b)$ , $bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b)$ 由欧几里得定理可知:$\gcd(a,b)=\gcd(b,a\bmod b)$ 所以 $ax_1+by_1=bx_2+(a\bmod b)y_2$ 又因为 $a\bmod b=a-(\lfloor\frac{a}{b}\rfloor\times b)$ 所以 $ax_1+by_1=bx_2+(a-(\lfloor\frac{a}{b}\rfloor\times b))y_2$ $ax_1+by_1=ay_2+bx_2-\lfloor\frac{a}{b}\rfloor\times by_2=ay_2+b(x_2-\lfloor\frac{a}{b}\rfloor y_2)$ 因为 $a=a,b=b$,所以 $x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2$ 将 $x_2,y_2$ 不断代入递归求解直至 $\gcd$(最大公约数,下同)为 $0$ 递归 $x=1,y=0$ 回去求解。 --- ### 实现 ```cpp int Exgcd(int a, int b, int &x, int &y) { if (!b) { x = 1; y = 0; return a; } int d = Exgcd(b, a % b, x, y); int t = x; x = y; y = t - (a / b) * y; return d; } ``` 函数返回的值为 $\gcd$,在这个过程中计算 $x,y$ 即可。 --- ### 值域分析 $ax+by=\gcd(a,b)$ 的解有无数个,显然其中有的解会爆 long long。 万幸的是,若 $b\not= 0$,扩展欧几里得算法求出的可行解必有 $|x|\le b,|y|\le a$。 下面给出这一性质的证明。 --- 证明 - $\gcd(a,b)=b$ 时,$a\bmod b=0$,必在下一层终止递归。 得到 $x_1=0,y_1=1$,显然 $a,b\ge 1\ge |x_1|,|y_1|$。 - $\gcd(a,b)\not= b$ 时,设 $|x_2|\le (a\bmod b),|y_2|\le b$。 因为 $x_1=y_2,y_1=x_2-{\left\lfloor\dfrac{a}{b}\right\rfloor}y_2$ 所以 $|x_1|=|y_2|\le b,|y_1|\le|x_2|+|{\left\lfloor\dfrac{a}{b}\right\rfloor}y_2|\le (a\bmod b)+{\left\lfloor\dfrac{a}{b}\right\rfloor}|y_2|$ $\le a-{\left\lfloor\dfrac{a}{b}\right\rfloor}b+{\left\lfloor\dfrac{a}{b}\right\rfloor}|y_2|\le a-{\left\lfloor\dfrac{a}{b}\right\rfloor}(b-|y_2|)$ $a\bmod b=a-{\left\lfloor\dfrac{a}{b}\right\rfloor}b\le a-{\left\lfloor\dfrac{a}{b}\right\rfloor}(b-|y_2|)\le a$ 因此 $|x_1|\le b,|y_1|\le a$ 成立。 --- ### 迭代法编写扩展欧几里得算法 首先,当 $x = 1$,$y = 0$,$x_1 = 0$,$y_1 = 1$ 时,显然有: $$ \begin{cases} ax + by & = a \\\\ ax_1 + by_1 & = b \end{cases} $$ 成立。 --- 已知 $a\bmod b = a - (\lfloor \frac{a}{b} \rfloor \times b)$,下面令 $q = \lfloor \frac{a}{b} \rfloor$。参考迭代法求 gcd,每一轮的迭代过程可以表示为: $$ (a, b) \rightarrow (b, a - qb) $$ 将迭代过程中的 $a$ 替换为 $ax + by = a$,$b$ 替换为 $ax_1 + by_1 = b$,可以得到: $$ \begin{aligned} & \begin{cases} ax + by & = a \\\\ ax_1 + by_1 & = b \end{cases} \\\\ \rightarrow & \begin{cases} ax_1 + by_1 & = b \\\\ a(x - qx_1) + b(y - qy_1) & = a - qb \end{cases} \end{aligned} $$ 据此就可以得到迭代法求 exgcd。 --- 因为迭代的方法避免了递归,所以代码运行速度将比递归代码快一点。 ```cpp int gcd(int a, int b, int& x, int& y) { x = 1, y = 0; int x1 = 0, y1 = 1, a1 = a, b1 = b; while (b1) { int q = a1 / b1; tie(x, x1) = make_tuple(x1, x - q * x1); tie(y, y1) = make_tuple(y1, y - q * y1); tie(a1, b1) = make_tuple(b1, a1 - q * b1); } return a1; } ``` 如果你仔细观察 $a_1$ 和 $b_1$,你会发现,他们在迭代版本的欧几里德算法中取值完全相同,并且以下公式无论何时(在 while 循环之前和每次迭代结束时)都是成立的:$x \cdot a +y \cdot b =a_1$ 和 $x_1 \cdot a +y_1 \cdot b= b_1$。因此,该算法肯定能正确计算出 $\gcd$。 最后我们知道 $a_1$ 就是要求的 $\gcd$,有 $x \cdot a +y \cdot b = g$。 --- #### 矩阵的解释 对于正整数 $a$ 和 $b$ 的一次辗转相除即 $\gcd(a,b)=\gcd(b,a\bmod b)$ 使用矩阵表示如 $$ \begin\{bmatrix\} b\\\\ a\bmod b \end\{bmatrix\} \= \begin\{bmatrix\} 0&1\\\\1&-\lfloor a/b\rfloor \end\{bmatrix\} \begin\{bmatrix\} a\\\\b \end\{bmatrix\} $$ 其中向下取整符号 $\lfloor c\rfloor$ 表示不大于 $c$ 的最大整数。我们定义变换 $\begin{bmatrix}a\\\\b\end{bmatrix}\mapsto \begin{bmatrix}0&1\\\\1&-\lfloor a/b\rfloor\end{bmatrix}\begin{bmatrix}a\\\\b\end{bmatrix}$。 --- 易发现欧几里得算法即不停应用该变换,有 $$ \begin{bmatrix} \gcd(a,b)\\\\0 \end{bmatrix} \= \left( \cdots \begin{bmatrix} 0&1\\\\1&-\lfloor a/b\rfloor \end{bmatrix} \begin{bmatrix} 1&0\\\\0&1 \end{bmatrix} \right) \begin{bmatrix} a\\\\b \end{bmatrix} $$ --- 令 $$ \begin{bmatrix} x_1&x_2\\\\x_3&x_4 \end{bmatrix} = \cdots \begin{bmatrix} 0&1\\\\1&-\lfloor a/b\rfloor \end{bmatrix} \begin{bmatrix} 1&0\\\\0&1 \end{bmatrix} $$ 那么 $$ \begin{bmatrix} \gcd(a,b)\\\\0 \end{bmatrix} = \begin{bmatrix} x_1&x_2\\\\x_3&x_4 \end{bmatrix} \begin{bmatrix} a\\\\b \end{bmatrix} $$ --- 满足 $a\cdot x_1+b\cdot x_2=\gcd(a,b)$ 即扩展欧几里得算法,注意在最后乘了一个单位矩阵不会影响结果,提示我们可以在开始时维护一个 $2\times 2$ 的单位矩阵编写更简洁的迭代方法如 ```cpp int exgcd(int a, int b, int &x, int &y) { int x1 = 1, x2 = 0, x3 = 0, x4 = 1; while (b != 0) { int c = a / b; std::tie(x1, x2, x3, x4, a, b) = std::make_tuple(x3, x4, x1 - x3 * c, x2 - x4 * c, b, a - b * c); } x = x1, y = x2; return a; } ``` 这种表述相较于递归更简单。 --- ## 应用 - [10104 - Euclid Problem](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1045) - [GYM - (J) once upon a time](http://codeforces.com/gym/100963) - [UVa - 12775 - Gift Dilemma](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4628)