筛法
考虑一个经典问题:求不大于 n 的所有素数
Eratosthenes 筛法(埃氏筛)
可能是因为名字太长太难念了所以才叫埃氏筛吧
算法原理
- 初始时令列表 \(A = \{ 2,3,...,n \}; \ p = 2\)
- 枚举所有 \(p\) 的倍数 (不包括 \(p\)),并在 \(A\) 中删去这些数
- 令 \(p\) 为 \(A\) 中的下一个数并跳转至 \((2)\)。如果不存在下一个则结束。
- 算法结束时,\(A\) 中剩下的数为不大于 \(n\) 的所有素数。
code
int sieve(int n, bool isprime[], int prime) {
int tot = 0;
for (int i = 2; i <= n; i++) isprime[i] = 1;
for (int i = 2; i <= n; i++) {
if (isprime[i]) {
prime[++tot] = i;
for (int j = i + i; j <= n; j += i)
isprime[j] = 0;
}
}
return tot;
}
如何 O(n) 求 1 ∼ n 的素数?
把筛法做到 \(O(n)\),每个合数必须只被筛去一次。
考虑 \(dp\) 求 \(1 ∼ n\) 每个数的最小质因子 \(f[]\)
如果已经知道 \(f[i]\),那么枚举 \(1 ∼ f[i]\) 的所有素数 \(p\),就可以求得 \(f[i·p] = p\)
容易看出每个 \(f[i]\) 只会被求一次
如果枚举至 \(i\) 时还未求出 \(f[i]\),则 \(i\) 不存在小于 \(i\) 的质因子,即 \(i\) 为质数。
这就是传说中的欧拉筛,也叫线性筛
int erlur(int n, int f[], int prime) {
int tot = 0;
for (int i = 2; i <= n; i++) {
if (!f[i]) prime[++tot] = f[i] = i;
for (int j = 1; j <= tot; j++) {
int t = i * prime[j];
if (t > n) break;
f[t] = prime[j];
if (f[i] == prime[j]) break;
}
}
}