埃氏筛和欧拉筛
埃氏筛
比较原始,但很实用,性价比高。
最简单的实现如下:(筛出1~N的质数)1
2
3
4
5
6
7bool notp[N];
void is() {
for (int i = 2;i <= N;++i)
if (!notp[i])
for (int j = 2*i;j <= N;j += i)
notp[j] = 1;
}
当然这有很多的优化空间,比如排除偶数、从i*i开始等等。
上面这段代码没有记录素数的数组。可以改为:1
2
3
4
5
6
7
8
9
10
11bool notp[N];
vector<int> pri;
void is{
for (int i = 2;i <= N;++i) {
if (!notp[i]) pri.push_back(i);
for (auto p:pri) {
if (i*p > N) break;
notp[i*p] = 1;
}
}
}
这里把筛去 “质数的整数倍” 变为 “整数的质数倍”,效果相同。
而且这种改法离欧拉筛就差一行代码
欧拉筛
也叫线性筛:1
2
3
4
5
6
7
8
9
10
11bool notp[i];
void els(){
for (int i = 2;i <= N;++i) {
if (!notp[i]) pri.push_back(i);
for (auto p : pri) {
if (i*p > N) break;
notp[i*p] = 1;
if (i % p == 0) break; //欧拉筛的精髓
}
}
}
每个数只筛一次
如 6=2*3=3*2。就让2先不筛它,让3去筛它。
12 = 2*6 = 3*4 = 4*3 = 6*2,实际上是6把他筛掉。
24 = 2*12 = 3*8 = 4*6 = 6*4 = 8*3 = 12*2,12去筛。
15 = 3*5 = 5*3 ,3筛掉。
这样看有点像保持第一个乘数不小于第二个乘数,且尽量保持第二个小的意思。
i 之前被 p 筛过了,由于 pri 里面质数是从小到大的,所以 i 乘上之后其他的质数 p’ 的结果一定会被 p 的倍数筛掉,就不需要在这里先筛一次,所以这里直接 break掉就好了 ——改自OI wiki
所以 i*p’ 一定会被之后的 i’*p 筛掉。
这里放一下 i 筛去的数来理解一下。1
2
3
4
5
6
7
82 4
3 6 9
4 8
5 10 15 20 25
6 12
7 14 21 28 35 42 49
8 16
9 18 27