![c8056d35cbcaf5a9d33bb7eaec3cd64e.png](https://img-blog.csdnimg.cn/img_convert/c8056d35cbcaf5a9d33bb7eaec3cd64e.png)
素数筛法,是一种快速“筛”出2~n之间所有素数的方法。朴素的筛法叫埃氏筛(the Sieve ofEratosthenes,埃拉托色尼筛),它的过程是这样的:
我们把2~n的数按顺序写出来:
从前往后看,找到第一个未被划掉的数,2,这说明它是质数。然后把2的倍数(不包括2)划掉:
下一个未被划掉的数是3,它是质数,把3的倍数划掉:
接下来应该是5,但是5已经超过
如何?是不是比一个一个判断快多了?这个过程写成代码就是:
bool isnp[MAXN]; // is not prime: 不是素数
void init(int n)
{
for (int i = 2; i * i <= n; i++)
if (!isnp[i])
for (int j = i * i; j <= n; j += i)
isnp[j] = 1;
}
代码也很简洁。这个算法的复杂度是
我们这次除了把2~n列出来,还维护一个质数表:
还是从头到尾遍历,第一个数是2,未被划掉,把它放进质数表:
然后我们用2去乘质数表里的每个数,划掉它们:
下一个是3,加入质数表,划掉6、9:
下一个是4(注意这里划掉的数也要遍历,只是不加入质数表),先划掉8,但我们不划掉12,因为12
实际上,对于
这么说有点抽象,具体地说,如这里的2能整除4,则
下一个是5,加入质数表,划掉10,15:
下一个是6,划掉12,6被2整除,跳过。
……
按这样的步骤进行下去,可以筛掉所有的合数,并得到一张质数表。
我们可以保证每个合数都被筛过,设任意合数
而由于一定有
代码如下(C++11风格):
bool isnp[MAXN];
vector<int> primes; // 质数表
void init(int n)
{
for (int i = 2; i <= n; i++)
{
if (!isnp[i])
primes.push_back(i);
for (int p : primes)
{
if (p * i > n)
break;
isnp[p * i] = 1;
if (i % p == 0)
break;
}
}
}
注意判断越界那里最好使用乘法而不是除法,一般不会溢出,计算机算除法比乘法要慢得多。
欧拉筛除了解决一些卡埃氏筛的毒瘤题(比如洛谷P3383,线性筛模板)外,在后续一些数论算法中还有更多的应用。
https://zhuanlan.zhihu.com/p/105467597zhuanlan.zhihu.com