目录
公因数的性质
对于两个正整数 (a,b) ,若有另外一个正整数 (d) 满足 (dmid a) 且 (dmid 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_iin 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) 一定有 (gmid a) 且 (gmid b)
其次证明其充分性:首先,作为 (a,b) 的因数, (g) 不可能含有以上 (m) 个质数以外的质因数
因此,不妨设 (d=g imes p_i) ,其中, (p_i) 是上面任意一个质数
再不妨设 (c_i<d_i)
因此, (min(c_i,d_i)=c_i)
所以 (d) 中含有 ((c_i+1)) 个 (p_i) 质因子
故 (d mid a) 不为 (a,b) 公因数
原命题得证
最大公因数的因数的性质
可以证明:所有的公因数 (Leftrightarrow) 最大公因数的所有因数
证明:
我们记 (g=gcd(a,b)) 则显然 (gmid a,gmid b) 且不存在 (forall din Z,d>g) 且 (dmid a,dmid b)
我们先证明必要性:对于所有 (g) 的因数 (c) ,显然有 (cmid g) 。故 (cmid a,cmid 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_iin 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)})
设 (dmid a,dmid b) 则若 (d=p_1^{e_1}p_2^{e_2}p_3^{e_3}cdots p_m^{e_m})
故一定有 (forall e_ileq c_i,e_ileq d_i)
即 (e_ileq min(c_i,d_i))
因此 (dmid g)
最大公因数的互质性质
若 (g=gcd(a,b)) 则 (gcd({aover g},{bover 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_iin 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)})
( herefore egin{cases} {aover 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)} \ \ {bover 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({aover g},{bover 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) 满足 (amid m) 且 (bmid 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_iin 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_iin 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}={acdot bover gcd(a,b)})
更相减损术
出自《九章算数》,算法过程如下:
当 (a eq 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))
则 (gmid a,gmid b)
设 (a=kb+r,(0leq r<b))
故 (r=a\%b)
又由 (gmid a=gmid(kb+r),gmid bRightarrow gmid kb) 得 (gmid (a-kb)Rightarrow gmid 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;