线性筛法
线性筛法(也称为 \(Euler\) 筛法(欧拉筛法)) 最常用!
1 | void init(int n) { |
\(Eratosthenes\) 筛法
\(Eratosthenes\) 筛法即埃拉托斯特尼筛法, 简称埃氏筛法。时间复杂度是 \(O(n\log\log n)\)。
1 | bool is_prime[N]; |
筛至平方根到 \(O(n \ln \ln \sqrt n + o(n))\)
1 | bool is_prime[N]; |
1 优化版
只筛奇数 (因为除 2 以外的偶数都是合数,所以我们可以直接跳过它们,只用关心奇数就好。
首先,这样做能让我们内存需求减半;其次,所需的操作大约也减半。)
1 |
|
分块筛选
占用内存及其少!!! 以下实现使用块筛选来计算小于等于 n
的质数数量。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29int count_primes(int n) {
const int S = 10000;
vector<int> primes;
int nsqrt = sqrt(n);
vector<char> is_prime(nsqrt + 1, true);
for (int i = 2; i <= nsqrt; i++) {
if (is_prime[i]) {
primes.push_back(i);
for (int j = i * i; j <= nsqrt; j += i) is_prime[j] = false;
}
}
int result = 0;
vector<char> block(S);
for (int k = 0; k * S <= n; k++) {
fill(block.begin(), block.end(), true);
int start = k * S;
for (int p : primes) {
int start_idx = (start + p - 1) / p;
int j = max(start_idx, p) * p - start;
for (; j < S; j += p) block[j] = false;
}
if (k == 0) block[0] = block[1] = false;
for (int i = 0; i < S && start + i <= n; i++) {
if (block[i]) result++;
}
}
return result;
}
块大小 \(S\) 取 \(10^4\) 到 \(10^5\) 之间,可以获得最佳的速度。