【数论】——质数
概念
- 对于一正整数 a,如果 a 的因数有且仅有 1 和 a,则称 a 为质数(素数),否则称 a 为合数。
质数定理
- 在整个自然数集合中,质数分布相对稀疏,对于一个足够大的数 N,[1,N] 中所有的质数个数为:
C o u n t O f P r i m e s = N ln N CountOfPrimes = \frac{N}{\ln N} CountOfPrimes=lnNN
质数的判定
试除法
-
模版题AcWing 866. 试除法判定质数
枚举 [2,x] 所有整数,如果x%i == 0
则 x 不是质数。 -
优化
容易发现,合数 x 的因数总是成对出现,并且关于 x \sqrt{x} x 对称,因此只将枚举区间缩小至 [2, x \sqrt{x} x] 即可,每次判断 x 是否为质数时间复杂度为严格 O( x \sqrt{x} x)。 -
代码
bool is_prime(int x)
{
if (x <= 1)
return false;
/*
这里建议写 i<=x/i
原因:
1)写 sqrt(x),要调用 <cmath> 的函数,太慢、
2)写 i*i<=x, 如果 i 过大会溢出。
*/
for (int i = 2; i <= x / i; i++) {if (x % i == 0)
return false;
}
return true;
}
质数的筛选
埃氏筛法
-
对于任意 x,其倍数 2x,3x… 必然不是质数, 从 2 开始扫描所有小于 N 的数,如果当前数为质数,筛除其小于等于 N 的所有倍数。直到筛去 [1,N] 中的所有质数的倍数,就可以筛去区间内所有合数。
-
优化:
由于对于数字 6,会被 2,3 各筛一遍,因此若当前数 x 为质数,小于x^2
的所有合数已经之前的质数筛去了,只需要筛除 [ x 2 x^2 x2, [ N x \frac{N}{x} xN] ] 的所有 x 的倍数即可。 -
筛除图解——(来自 李煜东 《算法竞赛进阶指南》)
-
复杂度分析
每次将 x 的倍数筛除,需要计算:
c n t = N 2 + N 3 + . . . + N N − 1 + N N cnt=\frac{N}{2}+\frac{N}{3}+...+\frac{N}{N-1}+\frac{N}{N} cnt=2N+3N+...+N−1N+NN
c
n
t
=
N
(
1
2
+
1
3
+
.
.
.
+
1
N
−
1
+
1
N
)
cnt = N\left(\frac{1}{2}+\frac{1}{3}+...+\frac{1}{N-1}+\frac{1}{N} \right)
cnt=N(21+31+...+N−11+N1)
此式为 调和级数:
c
n
t
=
ln
N
+
C
cnt = \ln N+{C}
cnt=lnN+C
因此复杂度为:O(
n
⋅
log
(
ln
n
)
n\cdot \log \left( \ln n \right)
n⋅log(lnn))。
- 代码
bool st[N]; // 表示是(true)否被筛
int primes[N], cnt = 0; //primes 存储所有质数,cnt 表示当前质数个数
void Eratosthenes(int n)
{for (int i = 2; i <= n; i++) {if (st[i])
continue;
primes[++cnt] = i; // 如果没被筛过,就是质数
for (int j = i; j <= n / i; j++) // 从 x^2 开始筛去 x 的倍数
st[j * i] = true;
}
}
线性筛法
-
上述从 x 2 x^2 x2 开始还是会重复筛除,eg:12 ,被 2,3,6 重复标记,因此需要对产生 12 的方式进行固定,只让其被筛除一次以达到线性复杂度 (O(n))。
-
线性筛法,是将一个数 x,只使 x 被其的最小质因数筛除,eg:12 仅被 2 筛除。
-
实现方式:
在埃式筛法的基础上,每次扫描不大于 n i \frac{n}{i} in 的所有质数,将st[primes[j] * i ] = true
筛除。 -
筛除图解——(来自 李煜东 《算法竞赛进阶指南》)!
-
代码
bool st[N]; // 表示是(true)否被筛
int primes[N], cnt = 0; //primes 存储所有质数,cnt 表示当前质数个数
void get_primes(int n)
{for (int i = 2; i <= n; i++) {if (!st[i])
primes[++cnt] = i; // 如果没被筛过,就是质数
for (int j = 0; primes[j] <= n / i; j++){if (i % primes[j] == 0) // 如果可以整除,则存在比当前更小的 prime 可以筛去 i,退出
break;
st[primes[j] * i] = true; //primes[j] 是 primes[j] * i 的最小质因子
}
}
}