如题目>▽<
一、埃氏筛:
朴素的筛法叫埃氏筛(the Sieve ofEratosthenes,埃拉托色尼筛),它的过程是这样的:
我们把2~n的数按顺序写出来:
从前往后看,找到第一个未被划掉的数,2,这说明它是质数。然后把2的倍数(不包括2)划掉:
下一个未被划掉的数是3,它是质数,把3的倍数划掉:
接下来应该是5,但是5已经超过 根号16了,所以遍历结束,剩下未被划掉的都是素数:
如何?是不是比一个一个判断快多了?这个过程写成代码就是:
1 | bool isnp[MAXN]; // is not prime: 不是素数 |
代码也很简洁。这个算法的复杂度是 OnLOGLOGn ,还是非常优秀了。但是我们可能会发现,在筛的过程中我们会重复筛到同一个数,例如12同时被2和3筛到,30同时被2、3和5筛到。所以我们引入欧拉筛,也叫线性筛,可以在 O(n) 时间内完成对2~n的筛选。它的核心思想是:让每一个合数被其最小质因数筛到。
二、欧拉筛
我们这次除了把2~n列出来,还维护一个质数表:
还是从头到尾遍历,第一个数是2,未被划掉,把它放进质数表:
然后我们用2去乘质数表里的每个数,划掉它们:
下一个是3,加入质数表,划掉6、9:
下一个是4(注意这里划掉的数也要遍历,只是不加入质数表),先划掉8,但我们不划掉12,因为12 (12=2x6=3x4)应该由它的最小质因数2筛掉,而不是3。
具体地说,如这里的2能整除4,则 4=2x2,那么对于任何大于2的质数p ,都有4p=2x2p ,其中2是一个比 p 更小的质数,所以 4p 应该被2筛掉而不是被p 筛掉。
下一个是5,加入质数表,划掉10,15:
下一个是6,划掉12,6被2整除,跳过。
……
按这样的步骤进行下去,可以筛掉所有的合数,并得到一张质数表。
代码如下(C++11风格):
1 | bool isnp[MAXN]; |
(卡c++11版本)
1 | bool isnp[P]; |
(超短风格)
1 | void get_prime(int n){//n是筛的范围,超1e8就不好使了 |
注意判断越界那里最好使用乘法而不是除法,一般不会溢出,计算机算除法比乘法要慢得多。
欧拉筛除了解决一些卡埃氏筛的毒瘤题比如洛谷P3383,线性筛模板外,在后续一些数论算法中还有更多的应用。