约数与倍数

约数与倍数

定义

aba|b("|"为整除符号)即 b0(mod a)b \equiv 0(\bmod\ a),则称 aabb 的约数,也称因数,而 bbaa 的倍数。

性质(整数惟一分解定理的推论)

任何正整数都是其本身的约数,也是其本身的倍数。
11 是任何正整数的约数,任何正整数都是 11 的倍数。

唯一质因数分解定律:若整数 N2N≥2,那么 N=p1r1p2r2pkrkN=p_1^{r_1} p_2^{r_2}\dots p_k^{r_k}pip_i 为素数,ri0r_i\geq 0),由此可得:

NN 的正约数集合为:{xx=p1b1p2b2pkbk}\{x|x=p_1^{b_1} p_2^{b_2} \dots p_k^{b_k}\}0biribiN0\leq b_i\leq r_i,b_i\in N^*
NN 的正约数个数为:
(r1+1)×(r2+1)××(rk+1)=i=1k(ri+1)(r_1+1)\times (r_2+1)\times \dots \times(r_k+1)=\prod\limits_{i=1}^k (r_i+1)
除了完全平方数,约数总是成对出现的,即 dnd\leq \sqrt nNdn{N\over d} \leq \sqrt n 都是 NN 的约数。
NN 的约数个数上界为 2N2\sqrt N
NN 的所有正约数的和为:
(1+p1+p12++p1r1)××(1+pk+pk2++pkrk)=i=1k(j=0ri(pi)j)(1+p_1+p_1^2+\dots+p_1^{r_1})\times\dots\times(1+p_k+p_k^2+\dots+p_k^{r_k})=\prod\limits_{i=1}^k (\sum\limits_{j=0}^{r_i} (p_i)^j)

最大公约数

定义

aabb 是不都为 00 的整数,cc 为满足 cac|acbc|b 的最大整数,则称 ccaabb 的最大公约数,记为 gcd(a,b)gcd(a,b)(a,b)(a,b)

性质

最大公约数具有以下性质;

gcd(a,b)=gcd(b,a)=gcd(a,b)=gcd(a,b)gcd(a,b)=gcd(b,a)=gcd⁡(−a,b)=gcd(|a|,|b|)
1gcd(a,b)min(a,b)1\leq gcd(a,b)\leq \min(a,b)
dad|adbd|b,则 dgcd(a,b)d|gcd(a,b)("|"是整除符号)
gcd(a,0)=agcd(a,0)=a
gcd(a,ka)=a(kZ)gcd⁡(a,ka)=a (k\in Z)

求解算法

方法一:直接暴力枚举,过程如下:

min(a,b)\min⁡(a,b) 到 11 枚举 xx,并判断 xx 是否能同时整除 aa 和 bb,如果可以则输出 xx 退出循环。

时间复杂度为 O(min(a,b))O(\min⁡(a,b))

方法二:分解素因数,过程如下:

a=p1r1p2r2pkrk b=p1s1p2s2pkska=p_1^{r_1}p_2^{r_2}\dots p_k^{r_k} ,b=p_1^{s_1}p_2^{s_2}\dots p_k^{s_k}ri,si0r_i,s_i \geq 0 且不会同时为 0 (1in)0 (1\leq i\leq n),则 gcd(a,b)=p1min(r1s1)p2min(r2s2)pkmin(rksk)gcd(a,b)=p_1^{\min(r1,s1)}p_2^{\min(r2,s2)}\dots p_k^{\min(rk,sk)}

代码如下:

int Decompose(int a, int b) {
	int ans = 1;
	for(int x = 2; x * x <= min(a,b); x++){
		while(a % x == 0 && b % x == 0) a /= x, b /= x, ans *= x;
		while(a % x == 0) a /= x;
		while(b % x == 0) b /= x;
	} 
	if (a % b == 0) ans *= b;
	else if (b % a == 0) ans *= a;
	return ans;
}

方法三:欧几里得算法:

“两个整数 a,b(ab)a,b(a\geq b) 的公约数集合与 aba−bbb 的公约数集合相同”,可得:gcd(a,b)=gcd(b,a mod b)gcd⁡(a,b)=gcd⁡(b,a \bmod b),俗名曰“辗转相减法”。

int gcd(int a, int b) {
	if (b == 0) return a;
    if (a < b) return gcd(b, a);
	return gcd(b, a - b);
}

如果输入一组数 1 1000000000,可以设想该算法的执行效率,因为每一次都会直接将 bb 减少 11,所以会减 10910^9 次才能使其为 00,效率甚至不如暴力枚举,那么,我们可以用模运算来代替减,就是小学奥数中常说的“辗转相除法”!代码如下:

int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
    // 或者可以写三目运算符成如下形式
    // return (b == 0) ? a : gcd(b, a % b);
}

时间复杂度分析

根据 gcd(a,b)=gcd(b,a mod b)gcd(a,b)=gcd(b,a \bmod b),设 a>ba>b

  • a2ba\geq 2b 时,ba/2b\leq a/2bb 的规模至少缩小一半;
  • a<2ba<2b 时,a mod b<a/2a \bmod b<a/2,余数的规模至少缩小一半;

所以时间复杂度为 O(loga+b)O(\log a+b),在实际使用中,复杂度远远达不到如此。

方法四:库函数

当然,我相信没有人会像写这个东西,所以 c++ 提供了库函数 __gcd(),其用法与上面的 gcd 函数一样,传入两个参数 aabb 即可返回 aabb 的最大公约数,要注意的是,这个函数属于 algorithm 库,而不是 cmath,用之前记得要写头文件。

比如 int res=__gcd(12,18),那么 resres 的数值就被更改为了 66

最坏时间复杂度也为 O(loga+b)O(\log a+b),所以很少有人闲的没事干写 gcd 函数,直接 __gcd(a,b) 就行了。

最小公倍数

定义

aabb 最小的正公倍数为 aabb 的最小公倍数,记作 lcm(a,b)lcm(a,b)

性质

最小公倍数有如下性质:

lcm(a,b)=ab/gcd(a,b)lcm(a,b)=ab/gcd(a,b)
ama|mbmb|m,则 lcm(a,b)mlcm(a,b)|m
m,a,bm,a,b 是正整数,则 lcm(ma,mb)=mlcm(a,b)lcm(ma,mb)=m∗lcm(a,b)

求解算法

  1. 两个数的最小公倍数

利用性质:lcm(a,b)=ab/gcd(a,b)lcm(a,b)=ab/gcd(a,b),可写出代码:

int lcm(int a,int b) {
    long long res=a*b;
    res/=__gcd(a,b);
    return res;
}
  1. 多个数的最小公倍数

依照求两个数最小公倍数的方法类推,可以先求 a1,a2a_1,a_2 的最小公倍数 b1b_1,再求 b1b_1a3a_3 的最小公倍数 b2b_2,再求 b2b_2a4a_4 的最小公倍数 b3b_3 \dots\dots

代码如下:

long long lcm(int a[],int n) {
	long long ans=1;
	for(int i=1;i<=n;++i)
		ans=ans*a[i]/__gcd(ans,1ll*a[i]);
	return ans;
}

练习题

P1463 [POI2001] [HAOI2007] 反素数
P1072 [NOIP2009 提高组] Hankson 的趣味题
[BZOJ 1876] Super GCD

赞赏