最大公约数、最小公倍数的两种求法
目录
公因数的性质
对于两个正整数 \(a,b\) ,若有另外一个正整数 \(d\) 满足 \(d\mid a\) 且 \(d\mid b\) 则称呼 \(d\) 为 \(a,b\) 的公因数
在 \(a,b\) 的所有公因数中,最大的被称呼为最大公因数,我们记为 \((a,b)\) 或 \(gcd(a,b)\)
最大公因数在质数分解上的性质
不妨设 \(\displaystyle a=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m},b=p_1^{d_1}p_2^{d_2}p_3^{d_3}\cdots p_m^{d_m}\)
其中, \(\forall c_i,d_i\in N\) 且 \(c_i>0\) 或 \(d_i>0\)
则 \(\displaystyle gcd(a,b)=p_1^{min(c_1,d_1)}p_2^{min(c_2,d_2)}p_3^{min(c_3,d_3)}\cdots p_m^{min(c_m,d_m)}\)
先证明必要性:显然,满足这个式子的正整数 \(g\) 一定有 \(g\mid a\) 且 \(g\mid b\)
其次证明其充分性:首先,作为 \(a,b\) 的因数, \(g\) 不可能含有以上 \(m\) 个质数以外的质因数
因此,不妨设 \(d=g\times p_i\) ,其中, \(p_i\) 是上面任意一个质数
再不妨设 \(c_i<d_i\)
因此, \(min(c_i,d_i)=c_i\)
所以 \(d\) 中含有 \((c_i+1)\) 个 \(p_i\) 质因子
故 \(d\nmid a\) 不为 \(a,b\) 公因数
原命题得证
最大公因数的因数的性质
可以证明:所有的公因数 \(\Leftrightarrow\) 最大公因数的所有因数
证明:
我们记 \(g=gcd(a,b)\) 则显然 \(g\mid a,g\mid b\) 且不存在 \(\forall d\in Z,d>g\) 且 \(d\mid a,d\mid b\)
我们先证明必要性:对于所有 \(g\) 的因数 \(c\) ,显然有 \(c\mid g\) 。故 \(c\mid a,c\mid b\) 。因此 \(c\) 为 \(a,b\) 的公因数
充分性的证明我们考虑将 \(g\) 按质因数分解:
设 \(\displaystyle a=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m},b=p_1^{d_1}p_2^{d_2}p_3^{d_3}\cdots p_m^{d_m}\)
其中, \(\forall c_i,d_i\in N\) 且 \(c_i>0\) 或 \(d_i>0\)
则 \(\displaystyle gcd(a,b)=p_1^{min(c_1,d_1)}p_2^{min(c_2,d_2)}p_3^{min(c_3,d_3)}\cdots p_m^{min(c_m,d_m)}\)
设 \(d\mid a,d\mid b\) 则若 \(d=p_1^{e_1}p_2^{e_2}p_3^{e_3}\cdots p_m^{e_m}\)
故一定有 \(\forall e_i\leq c_i,e_i\leq d_i\)
即 \(e_i\leq min(c_i,d_i)\)
因此 \(d\mid g\)
最大公因数的互质性质
若 \(g=gcd(a,b)\) 则 \(gcd({a\over g},{b\over g})=1\)
证明:\(\displaystyle a=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m},b=p_1^{d_1}p_2^{d_2}p_3^{d_3}\cdots p_m^{d_m}\)
其中, \(\forall c_i,d_i\in N\) 且 \(c_i>0\) 或 \(d_i>0\)
则 \(\displaystyle gcd(a,b)=p_1^{min(c_1,d_1)}p_2^{min(c_2,d_2)}p_3^{min(c_3,d_3)}\cdots p_m^{min(c_m,d_m)}\)
\(\therefore \begin{cases} {a\over g}=p_1^{c_1-min(c_1,d_1)}p_2^{c_2-min(c_2,d_2)}p_3^{c_3-min(c_3,d_3)}\cdots p_m^{c_m-min(c_m,d_m)} \\\ \\ {b\over g}=p_1^{d_1-min(c_1,d_1)}p_2^{d_2-min(c_2,d_2)}p_3^{d_3-min(c_3,d_3)}\cdots p_m^{d_m-min(c_m,d_m)} \end{cases}\)
故 \(\displaystyle gcd({a\over g},{b\over g})=\prod_{i=1}^mp_i^{min(\ c_i-min(c_i,d_i),d_i-min(c_i,d_i)\ )}=\prod_{i=1}^mp_i^{min(c_i,d_i)-min(c_i,d_i)}=\prod_{i=1}^mp_i^0=1\)
公倍数的性质
对于两个正整数 \(a,b\) ,若有另外一个正整数 \(m\) 满足 \(a\mid m\) 且 \(b\mid m\) 则称呼 \(m\) 为 \(a,b\) 的公倍数
在 \(a,b\) 的所有公倍数中,最小的被称呼为最小公倍数,我们记为 \([a,b]\) 或 \(lcm(a,b)\)
同上,可以证得:
1.若 \(\displaystyle a=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m},b=p_1^{d_1}p_2^{d_2}p_3^{d_3}\cdots p_m^{d_m}\)
其中, \(\forall c_i,d_i\in N\) 且 \(c_i>0\) 或 \(d_i>0\)
则 \(\displaystyle lcm(a,b)=p_1^{max(c_1,d_1)}p_2^{max(c_2,d_2)}p_3^{max(c_3,d_3)}\cdots p_m^{max(c_m,d_m)}\)
2.范围内,所有的公倍数 \(\Leftrightarrow\) 最小公倍数的所有倍数
额外的,可以证得如何用最大公因数求出最小公倍数:
设 \(\displaystyle a=p_1^{c_1}p_2^{c_2}p_3^{c_3}\cdots p_m^{c_m},b=p_1^{d_1}p_2^{d_2}p_3^{d_3}\cdots p_m^{d_m}\)
其中, \(\forall c_i,d_i\in N\) 且 \(c_i>0\) 或 \(d_i>0\)
则 \(\displaystyle gcd(a,b)=p_1^{min(c_1,d_1)}p_2^{min(c_2,d_2)}p_3^{min(c_3,d_3)}\cdots p_m^{min(c_m,d_m)}\)
其次,还有 \(\displaystyle lcm(a,b)=p_1^{max(c_1,d_1)}p_2^{max(c_2,d_2)}p_3^{max(c_3,d_3)}\cdots p_m^{max(c_m,d_m)}\)
由于 \(max(c_i,d_i)=c_i+d_i-min(c_i,d_i)\)
故 \(\displaystyle lcm(a,b)=\prod_{i=1}^mp_i^{max(c_i,d_i)}=\prod_{i=1}^mp_i^{c_i+d_i-min(c_i,d_i)}=\prod_{i=1}^mp_i^{c_i}\cdot \prod_{i=1}^mp_i^{d_i}\cdot(\prod_{i=1}^mp_i^{min(c_i,d_i)})^{-1}={a\cdot b\over gcd(a,b)}\)
更相减损术
出自《九章算数》,算法过程如下:
当 \(a\neq b\) 时,用大数减去小数,再比较这两数
重复上述操作,直至两数相等
所得数即为 \(a,b\) 的最大公因数
证明一下它的正确性:
我们在原算法的基础上再添一步: \(a=b\) 后, \(a\) 再减去一次 \(b\) ,此刻 \(a=0,b\) 为最大公因数
加上此步后,可以延拓至任意正整数 \(n\) 与 \(0\) 的最大公因数都为 \(n\)
记 \(g=gcd(a,b),a=Ag,b=Bg\) 则 \(gcd(A,B)=1\)
当 \(a=b\) 时 \(A=B\) 故 \(A=B=1\) 此时得到的为最大公因数
否则不妨设 \(A>B\) ,则 \(a>b\)
故 \(a\) 应该减去 \(b\) ,成为 \((A-B)g\)
若此时 \(a\) 仍大于 \(b\) ,则进而成为 \((A-2B)g\) ,重复执行后会成为 \((A\%B)g\)
这里的证明过程我们先跳过,避免和下面辗转相除法重复
可证得,重复执行后,一定能使得较大的变为 \(gcd(A,B)\cdot g=g\) ,较小的变为 \(0\)
下面是它的代码实现:
if(a<b) swap(a,b);
while(b!=0){
a-=b;
if(a<b) swap(a,b);
}
最坏情况下,输入 \(n\) 和 \(1\) ,复杂度为 \(O(n)\)
优化
根据《九章算数》:“可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。”
若 \(a,b\) 同时为偶数时, \(g\) 翻倍,且 \(a,b\) 同时减半
若 \(a,b\) 一个为偶数时,为偶数的减半
这样既不影响计算,又可以加快运行速度:
int g=1;
while(a%2==0&&b%2==0) a/=2,b/=2,g*=2;
if(a<b) swap(a,b);
while(b!=0){
a-=b;
while(a%2==0&&b%2==0) a/=2,b/=2,g*=2;
if(a<b) swap(a,b);
}
g*=a;
最坏情况下就是 \(n=(2^k-1)\) 与 \(1\) 时,复杂度 \(O(\log n)\)
当然,还会有其它的优化。由于和后者辗转相除法类似,就放到后面说了
辗转相除法
辗转相除法,又名欧几里得算法,由古希腊数学家欧几里得(Euclid)提出
对于非零的数 \(a\) 与数 \(b\) (如果一个为零,另一个直接就是答案)
设 \(g=gcd(a,b)\)
则 \(g\mid a,g\mid b\)
设 \(a=kb+r,(0\leq r<b)\)
故 \(r=a\%b\)
又由 \(g\mid a=g\mid(kb+r),g\mid b\Rightarrow g\mid kb\) 得 \(g\mid (a-kb)\Rightarrow g\mid r\)
因此,\(gcd(a,b)=gcd(b,a\%b)\)
代码实现:
int gcd(int a,int b){ return b?a:gcd(b,a%b); }
非递归写法:
while(b!=0){
int tmp=a%b;
a=b;
b=tmp;
}
考虑到非递归写法的实质是交换 \(a,b\) 并用交换后的 \(b\) 对 \(a\) 取模
故此可进一步写为:
while(b!=0){
swap(a,b);
b%=a;
}
又考虑到 swap
函数可以通过 a^=b^=a^=b
实现,且赋值语句返回值为所附值的 C++ 特性
最终压行为
if(!b) while(b^=a^=b^=a%=b);
复杂度均为 \(O(\log n)\)
优化
由于辗转相除法的复杂度已经较低,各个优化方案都只能优化常数
不过,幸运的是,这些优化方法对更相减损术也适用
同样用到《九章算术》提到的优化法,并加入二进制实现快速的乘除操作:
int g=1;
while(b!=0){
while( (a&1)==0&&(b&1)==0 ) a>>=1,b>>=1,g<<=1;
swap(a,b);
b%=a;
}
g*=a;
还可以使用 lowbit 进行优化运算
其中 \(lowbit(x)\) 函数,为取数 \(x\) 的最低二进制位函数
如 \(lowbit(6)=4,lowbit(34)=2\)
在 C++ 中,可用 (x&(-x))
实现
故引入 lowbit 优化运算:
int g=1;
while(b!=0){
g*=Min(a&(-a),b&(-b));
a/=(a&(-a));
b/=(b&(-b));
swap(a,b);
b%=a;
}
g*=a;