最大公约数、最小公倍数的两种求法

目录

目录地址

上一篇

下一篇


公因数的性质

对于两个正整数 \(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;
posted @ 2020-02-24 09:43  JustinRochester  阅读(839)  评论(0编辑  收藏  举报