前言
最近在学习数论的相关知识,特此整理一下。本篇主要介绍了约数的一些问题。
相关数学知识
约数定义
若整数 n n n 除以整数 d d d 的余数为0,即 d d d 能整除 n n n,则称 d d d 是 n n n 的约数。
算术基本定理的推论
在算数基本定理中,若正整数
N
N
N 被唯一分解为
N
=
p
1
c
1
p
2
c
2
⋯
p
n
c
m
N=p_1^{c_1}{p_2}^{c_2}\cdots {p_n}^{c_m}
N=p1c1p2c2⋯pncm,其中
c
i
c_i
ci 都是正整数,
p
i
p_i
pi 都是质数,且满足
p
1
<
p
2
<
⋯
<
p
m
p_1<p_2<\cdots<p_m
p1<p2<⋯<pm,则
N
N
N 的正约数集合可以写作:
V
=
{
p
1
b
1
p
2
b
2
⋯
p
m
b
m
}
V=\left\{p_1^{b_1}p_2^{b_2}\cdots p_m^{b_m}\right\}
V={p1b1p2b2⋯pmbm},其中
0
≤
b
i
≤
c
i
0\leq b_i \leq c_i
0≤bi≤ci
N
N
N 的正约数个数为:
(
c
1
+
1
)
(
c
2
+
1
)
⋯
(
c
m
+
1
)
=
∏
i
=
1
m
(
c
i
+
1
)
(c_1+1)(c_2+1) \cdots(c_m+1)=\prod \limits_{i=1}^m(c_i+1)
(c1+1)(c2+1)⋯(cm+1)=i=1∏m(ci+1)
N
N
N 的所有正约数之和
(
p
1
0
+
p
1
1
+
p
1
2
+
⋯
+
p
1
c
1
)
∗
⋯
∗
(
p
m
0
+
p
m
1
+
⋯
+
p
m
c
m
)
=
∏
i
=
1
m
(
∑
j
=
0
c
i
(
p
i
)
j
)
(p_1^{0}+p_1^{1}+p_1^{2}+\cdots+p_1^{c_1})*\cdots*(p_m^{0}+p_m^{1}+\cdots+p_m^{c_m})=\prod\limits_{i=1}^m(\sum\limits_{j=0}^{c_i}(p_i)^{j})
(p10+p11+p12+⋯+p1c1)∗⋯∗(pm0+pm1+⋯+pmcm)=i=1∏m(j=0∑ci(pi)j)
具体的证明方法后面会详细介绍。
试除法求公约数
试除法
若 d ≥ N d\geq\sqrt{N} d≥N 是 N N N 的约数,则 N / d ≤ N N/d\leq\sqrt{N} N/d≤N 也是 N N N 的约数。换言之,约数总是成对出现的(对于完全平方数, N \sqrt{N} N会单独出现)。
因此只需扫描 d = 1 d=1 d=1 到 d = N d=\sqrt{N} d=N 的约数,尝试 d d d 能否整除 N N N,若能整除,则 N / d N/d N/d 也是 N N N 的约数。时间复杂度为 O ( N ) O(\sqrt{N}) O(N)。
具体事例
给定
n
n
n
个正整数
a
i
a_i
ai,
对于每个整数
a
i
a_i
ai,
请你按照从小到大的顺序输出它的所有约数。
解决该问题的代码如下:
import java.io.*;
import java.util.*;
class Main {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bufferedReader.readLine());
for (int i = 0; i < n; i ++) {
int a = Integer.parseInt(bufferedReader.readLine());
var res = get_divisors(a);
for (int x : res) {
System.out.print(x + " ");
}
System.out.println();
}
bufferedReader.close();
}
public static List<Integer> get_divisors(int x) {
List<Integer> res = new ArrayList<>();
for (int i = 1; i <= x / i; i ++) {
if (x % i == 0) {
res.add(i);
//判断x是否为完全平方数
if (x / i != i) {
res.add(x / i);
}
}
}
Collections.sort(res);
return res;
}
}
这里利用一个List集合来存储一个数的约数。代码比较简单易懂。
约数的个数
约数个数推导
令 N = p 1 α 1 ∗ p 2 α 2 ∗ ⋯ ∗ p k α k N=p_1^{\alpha_1}*{p_2}^{\alpha_2}*\cdots* {p_k}^{\alpha_k} N=p1α1∗p2α2∗⋯∗pkαk
因为任意一个约数
d
d
d 可以表示为:
d
=
p
1
β
1
∗
p
2
β
2
∗
⋯
∗
p
k
β
k
d=p_1^{\beta_1}*p_2^{\beta_2}*\cdots*p_k^{\beta_k}
d=p1β1∗p2β2∗⋯∗pkβk,其中
0
≤
β
i
≤
α
i
0\leq\beta_i\leq\alpha_i
0≤βi≤αi
如果每一项的 β i \beta_i βi 不相同,那么约数 d d d 也就不相同(根据算数基本定理,每个数的因数分解是唯一的)。
所以
N
N
N 的约数就跟
β
i
\beta_i
βi的取法是一一对应的
其中,
β
1
\beta_1
β1 有
0
∼
α
1
0\sim\alpha_1
0∼α1 种取法;
β
2
\beta_2
β2 有
0
∼
α
2
0\sim\alpha_2
0∼α2 种取法;
⋯
⋯
\cdots\cdots
⋯⋯
β
k
\beta_k
βk 有
0
∼
α
k
0\sim\alpha_k
0∼αk 种取法;
因此,约数的个数为
(
α
1
+
1
)
∗
(
α
2
+
1
)
⋯
(
α
k
+
1
)
=
∏
i
=
1
k
(
α
i
+
1
)
(\alpha_1+1)*(\alpha_2+1) \cdots(\alpha_k+1)=\prod \limits_{i=1}^k(\alpha_i+1)
(α1+1)∗(α2+1)⋯(αk+1)=i=1∏k(αi+1)
具体事例
给定
n
n
n
个正整数
a
i
a_i
ai,
请你输出这些数的乘积的约数个数,答案对
1
0
9
+
7
10^9+7
109+7 取模。
实现代码如下:
import java.io.*;
import java.util.*;
class Main {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bufferedReader.readLine());
int mod = 1000000007;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i ++) {
int a = Integer.parseInt(bufferedReader.readLine());
for (int j = 2; j <= a / j; j ++) {
while (a % j == 0) {
a /= j;
//作用:当 Map 集合中有这个 key 时,就使用这个 key 值;如果没有就使用默认值 defaultValue
map.put(j, map.getOrDefault(j, 0) + 1);
}
}
if (a > 1) {
map.put(a, map.getOrDefault(a, 0) + 1);
}
}
long ans = 1;
for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
ans = ans * (entry.getValue() + 1) % mod;
}
System.out.println(ans);
}
}
这里面的Map集合存储的是每一个底数和底数对应的指数,而对于若干数乘积取约数可以通过分别对每一个乘数取约数进行求解。
约数之和
约数之和推导
约数之和推导相对简单,令 N = p 1 α 1 ∗ p 2 α 2 ∗ ⋯ ∗ p k α k N=p_1^{\alpha_1}*{p_2}^{\alpha_2}*\cdots* {p_k}^{\alpha_k} N=p1α1∗p2α2∗⋯∗pkαk
可以计算出 p 1 α 1 p_1^{\alpha_1} p1α1 的约数之和为 ( p 1 0 + p 1 1 + ⋯ + p 1 α 1 ) (p_1^{0}+p_1^{1}+\cdots+p_1^{\alpha_1}) (p10+p11+⋯+p1α1).
同理, p 2 α 2 p_2^{\alpha_2} p2α2 的约数之和为 ( p 2 0 + p 2 1 + ⋯ + p 2 α 2 ) (p_2^{0}+p_2^{1}+\cdots+p_2^{\alpha_2}) (p20+p21+⋯+p2α2).
⋯ ⋯ \cdots\cdots ⋯⋯
p k α k p_k^{\alpha_k} pkαk 的约数之和为 ( p k 0 + p k 1 + ⋯ + p k α k ) (p_k^{0}+p_k^{1}+\cdots+p_k^{\alpha_k}) (pk0+pk1+⋯+pkαk).
因此 N N N 的约数之和为:
( p 1 0 + p 1 1 + p 1 2 + ⋯ + p 1 α 1 ) ∗ ⋯ ∗ ( p k 0 + p k 1 + ⋯ + p k α k ) = ∏ i = 1 k ( ∑ j = 0 α i ( p i ) j ) (p_1^{0}+p_1^{1}+p_1^{2}+\cdots+p_1^{\alpha_1})*\cdots*(p_k^{0}+p_k^{1}+\cdots+p_k^{\alpha_k})=\prod\limits_{i=1}^k(\sum\limits_{j=0}^{\alpha_i}(p_i)^{j}) (p10+p11+p12+⋯+p1α1)∗⋯∗(pk0+pk1+⋯+pkαk)=i=1∏k(j=0∑αi(pi)j)
再利用程序求解约数之和的式子时,可以利用下面的方法:
t = t ∗ p + 1 t=t*p+1 t=t∗p+1
开始时:
t
=
1
t=1
t=1
t
=
p
+
1
t=p+1
t=p+1
t
=
p
2
+
p
+
1
t=p^2+p+1
t=p2+p+1
⋯
⋯
\cdots\cdots
⋯⋯
t
=
p
α
+
p
α
−
1
+
⋯
+
p
+
1
t=p^{\alpha}+p^{\alpha-1}+\cdots+p+1
t=pα+pα−1+⋯+p+1
具体事例
给定
n
n
n
个正整数
a
i
a_i
ai,
请你输出这些数的乘积的约数之和,答案对
1
0
9
+
7
10^9+7
109+7 取模。
实现代码如下:
import java.io.*;
import java.util.*;
class Main {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bufferedReader.readLine());
Map<Integer, Integer> map = new HashMap<>();
int mod = 1000000007;
for (int i = 0; i < n; i ++) {
int a = Integer.parseInt(bufferedReader.readLine());
for (int j = 2; j <= a / j; j ++) {
while (a % j == 0) {
a /= j;
map.put(j, map.getOrDefault(j, 0) + 1);
}
}
if (a > 1) {
map.put(a, map.getOrDefault(a, 0) + 1);
}
}
long ans = 1;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
long t = 1;
int a = entry.getKey();
int b = entry.getValue();
while (b -- > 0) {
t = (t * a + 1) % mod;
}
ans = ans * t % mod;
}
System.out.println(ans);
}
}