算法学习笔记(17): 素数筛

素数筛法,是一种快速“筛”出2~n之间所有素数的方法。朴素的筛法叫埃氏筛(the Sieve ofEratosthenes,埃拉托色尼筛),它的过程是这样的:

我们把2~n的数按顺序写出来:

\begin{matrix}2&3&4&5&6&7&8&9&10&11&12&13&14&15&16\end{matrix}

从前往后看,找到第一个未被划掉的数,2,这说明它是质数。然后把2的倍数(不包括2)划掉:

\begin{matrix}\color{blue}{2}&3&\cancel4&5&\cancel6&7&\cancel8&9&\cancel{10}&11&\cancel{12}&13&\cancel{14}&15&\cancel{16}\end{matrix}

下一个未被划掉的数是3,它是质数,把3的倍数划掉:

\begin{matrix}\color{blue}2&\color{blue}3&\cancel4&5&\cancel6&7&\cancel8&\cancel{9}&\cancel{10}&11&\cancel{12}&13&\cancel{14}&\cancel{15}&\cancel{16}\end{matrix}

接下来应该是5,但是5已经超过 \sqrt{16} 了,所以遍历结束,剩下未被划掉的都是素数:

\begin{matrix}\color{blue}2&\color{blue}3&\cancel4&\color{blue}5&\cancel6&\color{blue}7&\cancel8&\cancel{9}&\cancel{10}&\color{blue}{11}&\cancel{12}&\color{blue}{13}&\cancel{14}&\cancel{15}&\cancel{16}\end{matrix}

如何?是不是比一个一个判断快多了?这个过程写成代码就是:

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;
}

代码也很简洁。这个算法的复杂度是 O(n\log\log n) ,还是非常优秀了。但是我们可能会发现,在筛的过程中我们会重复筛到同一个数,例如12同时被2和3筛到,30同时被2、3和5筛到。所以我们引入欧拉筛,也叫线性筛,可以在 O(n) 时间内完成对2~n的筛选。它的核心思想是:让每一个合数被其最小质因数筛到


我们这次除了把2~n列出来,还维护一个质数表

\begin{matrix}2&3&4&5&6&7&8&9&10&11&12&13&14&15&16\end{matrix} \\\text{primes:}\;()

还是从头到尾遍历,第一个数是2,未被划掉,把它放进质数表:

\begin{matrix}\color{blue}2&3&4&5&6&7&8&9&10&11&12&13&14&15&16\end{matrix} \\\text{primes:}\;(2,)

然后我们用2去乘质数表里的每个数,划掉它们:

\begin{matrix}\color{blue}2&3&\cancel4&5&6&7&8&9&10&11&12&13&14&15&16\end{matrix} \\\text{primes:}\;(2,)

下一个是3,加入质数表,划掉6、9:

\begin{matrix}\color{blue}2&\color{blue}3&\cancel4&5&\cancel6&7&8&\cancel9&10&11&12&13&14&15&16\end{matrix} \\\text{primes:}\;(2,3,)

下一个是4(注意这里划掉的数也要遍历,只是不加入质数表),先划掉8,但我们不划掉12,因为12 (12=2\times6=3\times4) 应该由它的最小质因数2筛掉,而不是3。

\begin{matrix}\color{blue}2&\color{blue}3&\cancel4&5&\cancel6&7&\cancel8&\cancel9&10&11&12&13&14&15&16\end{matrix} \\\text{primes:}\;(2,3,)

实际上,对于 x ,我们遍历到质数表中的 p ,且发现 p|x 时,就应当停止遍历质数表。因为:设 x=pr(r\ge p)p 本身是 x 的最小质因数),那么对于任意 p'>p ,有 p'x=pp'r=p(p'r) ,说明 p'x 的最小质因数不是 p' ,我们不应该在此划掉它。

这么说有点抽象,具体地说,如这里的2能被4整除,则 4=2\times2 ,那么对于任何大于2的质数 p ,都有 4p=2\times2p ,其中2是一个比 p 更小的质数,所以 4p 应该被2筛掉而不是被 p 筛掉。

下一个是5,加入质数表,划掉10,15:

\begin{matrix}\color{blue}2&\color{blue}3&\cancel4&\color{blue}5&\cancel6&7&\cancel8&\cancel9&\cancel{10}&11&12&13&14&\cancel{15}&16\end{matrix} \\\text{primes:}\;(2,3,5,)

下一个是6,划掉12,6被2整除,跳过。

……

按这样的步骤进行下去,可以筛掉所有的合数,并得到一张质数表。

\begin{matrix}\color{blue}2&\color{blue}3&\cancel4&\color{blue}5&\cancel6&\color{blue}7&\cancel8&\cancel9&\cancel{10}&\color{blue}{11}&\cancel{12}&\color{blue}{13}&\cancel{14}&\cancel{15}&\cancel{16}\end{matrix} \\\text{primes:}\;(2,3,5,7,11,13)

我们可以保证每个合数都被筛过,设任意合数 x=pr(r\ge p) ,其中 px 的最小质因数,又设 r=p'r'(r'\ge p')p'r 的最小质因数。在处理 r 时,要遍历质数表,直到遇到 p' 时才结束,所以任意小于等于 p' 的质数与 r 的乘积,都会在此时被筛掉。

而由于一定有 p\le p' (因为 x 的最小质因数是 p ,而不是 p' ),所以在处理到 r 时, rp 一定会被筛到。

代码如下(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,线性筛模板)外,在后续一些数论算法中还有更多的应用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343