[数学基础] 3 筛法
筛法,个人觉得在基础阶段牢固的掌握埃氏筛和欧拉筛就很好了。它们是很好用的工具,用途也很广泛,甚至会用在一些(我)意想不到的地方。
筛法
1. 埃氏筛
- 主要思想:筛掉所有质数的倍数
- 代码
// 为什么j可以从i*i开始?
// 假设i=7, 那么比i小的所有的质数, 已经把2*7,3*7,5*7这样的数筛掉了, 所有可以直接从7*7开始筛
void Eratosthenes(){
for (int i=2;i<=n;++i) st[i] = 1;
for (int i=2;i<=n;++i){
if (st[i]){
p[++cnt] = i;
if ((ll)i*i > n) continue;
for (int j=i*i;j<=n;j+=i){
st[j] = false;
}
}
}
}
-
时间复杂度分析
如果是筛所有的数的倍数,那么会筛\(\frac{N}{2}+\frac{N}{3}+...+\frac{N}{N}=N\times(\frac{1}{2}+\frac{1}{3}+...+\frac{1}{N})\) 次。
由于调和级数\(1+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{N}=ln(N+1)+r\),\(r\)为欧拉常数,\(r\approx 0.5772156649\)。因此,时间复杂度可近似为\(O(NlnN)<O(NlogN)\)。
当只筛质数时,由质数分布定理可得\(N\)中约有\(\pi(N)=\frac{N}{ln N}\)个质数,因此时间复杂度估算结果为\(O(\frac{NlnN}{lnN})\),即非常接近\(O(N)\)。在实际计算下,埃氏筛的时间复杂度为\(O(NloglogN)\)。
2. 欧拉筛
- 主要思想:每个合数只被筛一次的算法。
- 代码
void Euler(){
for (int i=2;i<=n;++i){
if (!st[i]) p[++cnt] = i;
for (int j=1;p[j]<=n/i;++j){
st[p[j] * i] = true;
if (i % p[j] == 0) break;
}
}
}
// 为什么每个合数只会被筛一次呢?
// 因为每个合数只会被它的最小质因子筛掉
// 从小到大枚举p[j],当i%p[j]==0时, break。
// 1. 当i%p[j]==0时。说明p[j]是i的最小质因子, 那么p[j]一定也是p[j]*i的最小质因子
// 2. 当i%p[j]!=0时, 说明p[j]比i的最小质因子还小, 那么p[j]一定是p[j]*i的最小质因子
- 时间复杂度:\(O(N)\)。