引子:
我如今想知道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