算法學習筆記(17): 素數(shù)篩

素數(shù)篩法枣抱,是一種快速“篩”出2~n之間所有素數(shù)的方法蓝晒。樸素的篩法叫埃氏篩(the Sieve ofEratosthenes氢烘,埃拉托色尼篩)烦却,它的過程是這樣的:

我們把2~n的數(shù)按順序?qū)懗鰜恚?/p>

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

從前往后看执桌,找到第一個未被劃掉的數(shù)鄙皇,2,這說明它是質(zhì)數(shù)鼻吮。然后把2的倍數(shù)(不包括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}

下一個未被劃掉的數(shù)是3育苟,它是質(zhì)數(shù),把3的倍數(shù)劃掉:

\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已經(jīng)超過 \sqrt{16} 了违柏,所以遍歷結(jié)束博烂,剩下未被劃掉的都是素數(shù):

\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: 不是素數(shù)
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) ,還是非常優(yōu)秀了馍惹。但是我們可能會發(fā)現(xiàn)躺率,在篩的過程中我們會重復篩到同一個數(shù),例如12同時被2和3篩到万矾,30同時被2悼吱、3和5篩到。所以我們引入歐拉篩良狈,也叫線性篩后添,可以在 O(n) 時間內(nèi)完成對2~n的篩選。它的核心思想是:讓每一個合數(shù)被其最小質(zhì)因數(shù)篩到薪丁。


我們這次除了把2~n列出來遇西,還維護一個質(zhì)數(shù)表

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

還是從頭到尾遍歷,第一個數(shù)是2严嗜,未被劃掉粱檀,把它放進質(zhì)數(shù)表:

\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去乘質(zhì)數(shù)表里的每個數(shù),劃掉它們:

\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漫玄,加入質(zhì)數(shù)表茄蚯,劃掉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(注意這里劃掉的數(shù)也要遍歷睦优,只是不加入質(zhì)數(shù)表)第队,先劃掉8,但我們不劃掉12刨秆,因為12 (12=2\times6=3\times4) 應該由它的最小質(zhì)因數(shù)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 ,我們遍歷到質(zhì)數(shù)表中的 p 家凯,且發(fā)現(xiàn) p|x 時缓醋,就應當停止遍歷質(zhì)數(shù)表终蒂。因為:設 x=pr(r\ge p)p 本身是 x 的最小質(zhì)因數(shù))交洗,那么對于任意 p'>p ,有 p'x=pp'r=p(p'r) 时捌,說明 p'x 的最小質(zhì)因數(shù)不是 p' 掂之,我們不應該在此劃掉它抗俄。

這么說有點抽象脆丁,具體地說,如這里的2能被4整除动雹,則 4=2\times2 槽卫,那么對于任何大于2的質(zhì)數(shù) p ,都有 4p=2\times2p 胰蝠,其中2是一個比 p 更小的質(zhì)數(shù)歼培,所以 4p 應該被2篩掉而不是被 p 篩掉。

下一個是5茸塞,加入質(zhì)數(shù)表躲庄,劃掉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整除,跳過禾唁。

……

按這樣的步驟進行下去效览,可以篩掉所有的合數(shù),并得到一張質(zhì)數(shù)表荡短。

\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)

我們可以保證每個合數(shù)都被篩過丐枉,設任意合數(shù) x=pr(r\ge p) ,其中 px 的最小質(zhì)因數(shù)掘托,又設 r=p'r'(r'\ge p') 瘦锹, p'r 的最小質(zhì)因數(shù)。在處理 r 時闪盔,要遍歷質(zhì)數(shù)表弯院,直到遇到 p' 時才結(jié)束,所以任意小于等于 p' 的質(zhì)數(shù)與 r 的乘積泪掀,都會在此時被篩掉听绳。

而由于一定有 p\le p' (因為 x 的最小質(zhì)因數(shù)是 p ,而不是 p' )异赫,所以在處理到 r 時椅挣, rp 一定會被篩到。

代碼如下(C++11風格):

bool isnp[MAXN];
vector<int> primes; // 質(zhì)數(shù)表
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靠抑,線性篩模板)外量九,在后續(xù)一些數(shù)論算法中還有更多的應用。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荠列,隨后出現(xiàn)的幾起案子类浪,更是在濱河造成了極大的恐慌,老刑警劉巖弯予,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戚宦,死亡現(xiàn)場離奇詭異,居然都是意外死亡锈嫩,警方通過查閱死者的電腦和手機受楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呼寸,“玉大人艳汽,你說我怎么就攤上這事《匝” “怎么了河狐?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瑟捣。 經(jīng)常有香客問我馋艺,道長,這世上最難降的妖魔是什么迈套? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任捐祠,我火速辦了婚禮,結(jié)果婚禮上桑李,老公的妹妹穿的比我還像新娘踱蛀。我一直安慰自己,他們只是感情好贵白,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布率拒。 她就那樣靜靜地躺著,像睡著了一般禁荒。 火紅的嫁衣襯著肌膚如雪猬膨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天呛伴,我揣著相機與錄音寥掐,去河邊找鬼。 笑死磷蜀,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的百炬。 我是一名探鬼主播褐隆,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剖踊!你這毒婦竟也來了庶弃?” 一聲冷哼從身側(cè)響起衫贬,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎歇攻,沒想到半個月后固惯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡缴守,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年葬毫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屡穗。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡贴捡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出村砂,到底是詐尸還是另有隱情烂斋,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布础废,位于F島的核電站汛骂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏评腺。R本人自食惡果不足惜帘瞭,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歇僧。 院中可真熱鬧图张,春花似錦、人聲如沸诈悍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侥钳。三九已至适袜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舷夺,已是汗流浹背苦酱。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留给猾,地道東北人疫萤。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像敢伸,于是被迫代替她去往敵國和親扯饶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355