前言
本博客中所有的 有解
均指 有整数解
。
所有数如果不说明值域都是整数。
算法简介
为什么叫欧几里得算法呢?当然是欧几里得发明了啦
他能干什么呢?
- 求一个两个数字的最大公约数
- 求关于 \(x,y\) 的二元一次不定方程 \(ax+by=\gcd(a,b)\) 的整数解
求最大公约数(gcd)
最大公约数即为 \(\operatorname{Greatest Common Divisor}\),常缩写为 \(\gcd\) 。
一组数的公约数,是指同时是这组数中每一个数的约数的数。而最大公约数,则是指所有公约数里面最大的一个。
我们一般用 \(\gcd(a,b)\) 或者 \((a,b)\) 来表示两个数字的最大公约数。
我们一般用 \(\operatorname{lcm}(a,b)\) 或者 \([a,b]\) 来表示两个数字的最小公倍数(Least Common Multiple, LCM)。
显然 \(\gcd(a,b)=\gcd(b,a\bmod b)\)
证明如下:
设 \(a=bq+r\) 并且 \(0\le r<b\),显然 \(r=a \bmod b\),
设 \(d\mid a\) 且 \(d\mid b\),\(d\) 就是 \(a,b\) 的公约数,
由 \(a=bq+r\) 得 \(\dfrac{a}{d}=q\times\dfrac{b}{d}+\dfrac{r}{d}\),
也就是 \(\dfrac{r}{d}=\dfrac{a}{d}-q\times\dfrac{b}{d}\)
因为 \(d\mid a,d\mid b\),所以 \(\dfrac{a}{d},q\times \dfrac{b}{d}\) 都是整数,
所以 \(\dfrac{r}{d}\) 也是整数,\(d\) 也是 \(b,a \bmod b\) 的公约数,
同理我们也可以设 \(e\mid b\) 且 \(e\mid a \bmod b\),
同理可以证明 \(e\) 也是 \(a,b\) 的公约数。
换句话说, \(a,b\) 的公约数都是 \(b,a \bmod b\) 的公约数,\(b,a \bmod b\) 的公约数也都是 \(a,b\) 的公约数,
也就是说 \(a,b\) 和 \(b,a \bmod b\) 的公约数都相同,那么它们的最大公约数也都相等,也就是 \(\gcd(a,b)=\gcd(b,a\bmod b)\)
代码
int gcd(int x,int y){
if(x%y==0) return y;
return gcd(x%y,y);
}
从递归式不难得出复杂度为 \(O\left(\log_2\max(a,b)\right)\) ,并且当 \(a,b\) 为斐波那契数列的相邻两项的时候,算法复杂度最坏。
这种做法又称为辗转相除法。
同时我们也发现以下几个结论(这些结论证明显然,这里就不写证明过程了):
- \(\gcd(a,b,c)=\gcd\left(\gcd\left(a,b\right),c\right)\)
- \(\operatorname{lcm}(a,b)\times\gcd(a,b)=a\times b\)
- \(\operatorname{lcm}\left(a,b,c\right)= \operatorname{lcm}\left( \operatorname{lcm}\left(a,b\right) , c \right)\)
这样我们就可以求出多个数的最大公约数以及单个数、多个数的最小公倍数了。
求二元一次不定方程的整数解(exgcd)
根据裴蜀定理,我们知道,关于 \(x,y\) 的二元一次不定方程 \(ax+by=\gcd(a,b)\) 有整数解。
证明略,我不会你自己看。
我们可以模仿计算最大公约数的方法来解这个方程。
假设有一个方程 \(ax_0+by_0=\gcd(a,b)\)
我们通过一次辗转相除,得到 \(bx_1+(a \bmod b)y_1=\gcd(a,b)\)
两式相减,得: \(ax_0+by_0-bx_1-(a \bmod b)y_1=0\)
又因为 \(a\bmod b=a-\lfloor\frac{a}{b}\rfloor\times b\)
所以 \(ax_0+by_0-bx_1-\left(a-\lfloor\frac{a}{b}\rfloor\right)\times by_1=0\)
展开得 \(ax_0+by_0-bx_1-ay_1+\lfloor\frac{a}{b}\rfloor\times by_1=0\)
化简得 \(a\left(x_0-y_1\right)+by_0-bx_1+\lfloor\frac{a}{b}\rfloor\times by_1=0\)
即 \(x_0=y_1,y_0=x_1-\lfloor\frac{a}{b}\rfloor\times y_1\)
递归边界就是 \(b=0\) ,此时返回 \(x=1,y=0\) 即可。
代码:
void exgcd(int a,int b,int &x,int &y){
if(!b){ x=1; y=0; return; }
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
return;
}
根据得到的两个解 \(x=x_0,y=y_0\) ,我们就可以得到这个方程的所有整数解:
显然这里的 \(k\) 是整数。
那么对于任意关于 \(x,y\) 方程 \(ax+by=c\) 该怎么解呢?
首先通过裴蜀定理判断它有没有解,如果有解那么肯定 \(\gcd(a,b)|c\) ,不妨设 \(c=n\gcd(a,b)\)
我们先解方程 \(ax+by=gcd(a,b)\) ,得到一个解 \(x=x_0,y=y_0\)
不难发现原方程也有一个解 \(x=nx_0,y=ny_0\)
解即为
复杂度和前面的一样,是 \(O\left(\log_2\max(a,b)\right)\).
应用
gcd除了求两个数字的最大公约数以外貌似我不知道其他功能,但是exgcd的功能非常强大。
这里只写我知道的吧...
- 求线性同余方程,因为 \(ax+by=c\) 等价于 \(ax\equiv c \pmod b\) ,这样可以得出在正整数范围内的 \(x\) 的最小值。
- 求逆元,因为 \(ax+by=1\) 等价于 \(ax\equiv 1 \pmod b\) ,由于存在逆元 \(a^{-1}\bmod b\) 所以 \(\gcd(a,b)=1\) ,也就是 \(ax+by=\gcd(a,b)\) 。
- exCRT会用到。