求约数
求一个数的所有约数,枚举
[
1
,
n
]
[1,\sqrt n]
[1,n] 的所有整数,能整除 n
的就是约数,另一半约数就是 n / i
.
vector<int> get_divisors(int n) {
vector<int> res;
for (int i = 1; i <= n / i; ++i) {
if (n % i == 0) {
res.push_back(i);
if (i != n / i) res.push_back(n / i);
}
}
return res;
}
约数个数
求约数个数基于约数个数定理:
对于一个大于
1
1
1 的正整数
n
n
n 可以分解质因数:
n
=
∏
i
=
1
k
p
i
a
i
=
p
1
a
1
⋅
p
2
a
2
⋅
⋯
⋅
p
k
a
k
n=\prod_{i=1}^kp_i^{a_i}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdots\cdot p_k^{a_k}
n=i=1∏kpiai=p1a1⋅p2a2⋅⋯⋅pkak
则
n
n
n 的正约数的个数是
d
(
n
)
=
∏
i
=
1
k
(
a
i
+
1
)
=
(
a
1
+
1
)
(
a
2
+
1
)
⋯
(
a
k
+
1
)
d(n)=\prod_{i=1}^k(a_i+1)=(a_1+1)(a_2+1)\cdots(a_k+1)
d(n)=i=1∏k(ai+1)=(a1+1)(a2+1)⋯(ak+1)
证明:
p 1 a 1 p_1^{a_1} p1a1 的约数有 p 1 0 , p 1 1 , p 1 2 , ⋯ , p 1 a 1 p_1^0,p_1^1,p_1^2,\cdots,p_1^{a_1} p10,p11,p12,⋯,p1a1 共 a 1 + 1 a_1+1 a1+1 个,同理 p k a k p_k^{a_k} pkak 的约数有 a k + 1 a_k+1 ak+1 个。
由乘法原理, n n n 的约数个数就是 ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a k + 1 ) (a_1+1)(a_2+1)\cdots(a_k+1) (a1+1)(a2+1)⋯(ak+1)
代码:
其实和质因数分解很相似,只要会质因数分解,就会求因数个数
int num_divisors(int n) {
int res = 1;
for (int i = 2; i <= n / i; ++i) {
if (n % i == 0) {
int s = 0;
while (n % i == 0) {
n /= i;
++s;
}
res *= s + 1;
}
}
if (n > 1) res *= 2;
return res;
}
约数之和
对于一个大于
1
1
1 的正整数
n
n
n 可以分解质因数:
n
=
∏
i
=
1
k
p
i
a
i
=
p
1
a
1
⋅
p
2
a
2
⋅
⋯
⋅
p
k
a
k
n=\prod_{i=1}^kp_i^{a_i}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdots\cdot p_k^{a_k}
n=i=1∏kpiai=p1a1⋅p2a2⋅⋯⋅pkak
n 的约数之和是
σ
(
n
)
=
(
p
1
0
+
p
1
1
+
p
1
2
+
⋯
+
p
1
a
1
)
(
p
2
0
+
p
2
1
+
p
2
2
+
⋯
+
p
2
a
2
)
⋯
(
p
k
0
+
p
k
1
+
p
k
2
+
⋯
+
p
k
a
k
)
\sigma(n)=(p_1^0+p_1^1+p_1^2+\cdots+p_1^{a_1})(p_2^0+p_2^1+p_2^2+\cdots+p_2^{a_2})\cdots(p_k^0+p_k^1+p_k^2+\cdots+p_k^{a_k})
σ(n)=(p10+p11+p12+⋯+p1a1)(p20+p21+p22+⋯+p2a2)⋯(pk0+pk1+pk2+⋯+pkak)
等比数列求和,上式可以写成
σ
(
n
)
=
p
1
a
1
+
1
−
1
p
1
−
1
×
p
2
a
2
+
1
−
1
p
2
−
1
×
⋯
×
p
k
a
k
+
1
−
1
p
k
−
1
\sigma(n)=\frac{p_1^{a_1+1}-1}{p_1-1}\times\frac{p_2^{a_2+1}-1}{p_2-1}\times\cdots\times\frac{p_k^{a_k+1}-1}{p_k-1}
σ(n)=p1−1p1a1+1−1×p2−1p2a2+1−1×⋯×pk−1pkak+1−1
代码:
int sum_divisors(int n) {
int res = 1;
for (int i = 2; i <= n / i; ++i) {
if (n % i == 0) {
int s = i;
while (n % i == 0) {
n /= i;
s *= i;
}
res *= (s - 1) / (i - 1);
}
}
if (n > 1) res *= (n * n - 1) / (n - 1);
return res;
}
最大公约数
欧几里得算法
欧几里得算法又称辗转相除法,计算公式为 gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,a\mod b) gcd(a,b)=gcd(b,amodb)
假如需要求 1997 1997 1997 和 615 615 615 两个正整数的最大公约数,用欧几里得算法,是这样进行的:
1997 ÷ 615 = 3 ( 余 152 ) 1997 ÷ 615 = 3 (余 152) 1997÷615=3(余152)
615 ÷ 152 = 4 ( 余 7 ) 615 ÷ 152 = 4(余7) 615÷152=4(余7)
152 ÷ 7 = 21 ( 余 5 ) 152 ÷ 7 = 21(余5) 152÷7=21(余5)
7 ÷ 5 = 1 ( 余 2 ) 7 ÷ 5 = 1 (余2) 7÷5=1(余2)
5 ÷ 2 = 2 ( 余 1 ) 5 ÷ 2 = 2 (余1) 5÷2=2(余1)
2 ÷ 1 = 2 ( 余 0 ) 2 ÷ 1 = 2 (余0) 2÷1=2(余0)
至此,最大公约数为 1 1 1
以除数和余数反复做除法运算,当余数为 0 0 0 时,取当前算式除数为最大公约数,所以就得出了 1997 1997 1997 和 615 615 615 的最大公约数 1 1 1。
代码:
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}