1.3 GCD、EXGCD、线性同余方程
最大公约数(GCD)和最小公倍数(LCM)的基本概念也是小学知识。对于任意整数 a , b a,b a,b,我们有一个很重要的公式,即 a ∗ b = G C D ( a , b ) ∗ L C M ( a , b ) a*b = GCD(a,b)*LCM(a,b) a∗b=GCD(a,b)∗LCM(a,b)。
首先我们会介绍几种基本的求GCD方式,虽然C++选手完全可以使用__gcd()函数去求,但自定义的求法往往更加灵活,方便优化以及改动实现其他功能。
辗转相除法求GCD
辗转相除法又称欧几里得算法,其原理是 G C D ( x , y ) = G C D ( x , y − x ) GCD(x,y)=GCD(x,y-x) GCD(x,y)=GCD(x,y−x),证明如下:
①设 z ∣ x z|x z∣x, z ∣ y z|y z∣y,则 z ∣ ( y − x ) z|(y-x) z∣(y−x)。
②设 z z z不是 x x x因子,则 z z z不是 x x x, y − x y-x y−x的公因子。
③设 z ∣ x z|x z∣x, z z z不是 y y y的因子,则 z z z不是 x x x, y − x y-x y−x的公因子。
当然我们可以把这个转换为模运算,因为取 y − x y-x y−x的前提是 y > x y>x y>x,于是我们可以递归的用模运算去实现,保证函数的第一个参数大于第二个参数(实在理解不了可以记模板)。代码如下。
typedef long long ll;
ll GCD(ll x, ll y) {
return y == 0 ? x : GCD(y,x % y);
}
二进制算法求GCD
这个算法比辗转相除法更快一点点,但这个优化是常数级别的。原理是通过不断去除因子2来降低常数。具体做法如下:
若 x = y x=y x=y,则 G C D ( x , y ) = x GCD(x,y)=x GCD(x,y)=x,否则:
①若 x , y x,y