歐拉篩法實現素數表的快速篩取

引子:
我如今想知道1——1e8的範圍內有多少個素數(質數),有什麼方法?
樸素法,對於每個數n,咱們判斷它是不是素數
像這樣:c++

bool check(int curr)
{
    int i;
    for(i = 2; i < sqrt(curr)+1; ++i)
    {
        if(curr % i == 0) return false;
    }
    return true;
}

對於每個數,咱們最多須要sqrt(n)次即可以斷定出它是素數仍是合數。
太慢了!這個時間複雜度已經能夠飛出天際了。markdown


咱們須要一種快速的方法篩取素數。
咱們觀察素數和合數最大的不一樣——合數有多個因數(除了1與自身),而素數不行。那麼當咱們得知對於int x,2 * x ,3 * x……便必定都是合數
看來這個效率很高,可是仍是不夠快。由於會出現判斷重複ui

那麼咱們便使用歐拉篩法來篩取素數。
前文的方法作了什麼無用功?例如20的判斷,咱們在2,4,5,10的時候都去斷定了一次,而歐拉篩法能夠保證20 只被斷定1次
如何保證?spa


先放出源代碼:code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000005;

int n, x[maxn], pri[maxn/10], tot, curr, num;//m之內的質數不會m/10個,不放心 || m很小的話能夠再開大一點

int main()
{
    //freopen("test.in", "r", stdin);
    scanf("%d%d", &n, &num);
    x[1] = 1;
    for(int i = 2; i != n+1; ++i)
    {
        if(!x[i])
        {
            pri[tot++] = i; //若是一個數是合數那麼它必定會在以前被斷定出來,故剩下的必定是質數
        }
        for(int j = 0; j != tot; ++j)//當前的全部質數
        {
            curr = pri[j]*i;//必定是合數
            if(curr > n)    break;//超出範圍
            x[curr] = 1;//置爲合數
            if(i % pri[j] == 0)    break;//重點
        }
    }
    fclose(stdin);
    return 0;
}

重點代碼:string

if(i % pri[j] == 0) break;

要說保證某數不被重複判斷,關鍵就在這行代碼上。
知足上式時,i是pri[j]的倍數,那麼對於後面的pri[k]來講,i * pri[k]就能夠被分解爲pri[j] * (i / pri[j] *pri[k]),而該數在以前i == pri[j]的時候已經出現過,因而就重複了。直接退出,避免了重複斷定it


歐拉篩法當然快,可是也有適用條件,例如求(n, n + 10)中的素數個數的時候,它就明顯慢了。因爲必須從1開始,因此歐拉篩法的用處在於直接打表而不是求某一段區間。對於這種問題,樸素法何嘗不可class

歐拉篩法的原理分析至此結束。
箜瑟_qi 2017.04.10 00:43test

相關文章
相關標籤/搜索