說明:篇中的n和N都是同一個意義,大小寫不過是爲了表現具體和一般形式而已,穿插着用可能讓讀者容易混淆,請多體諒。
一、質數定義
指在大於1的整數中,只能被1和它本身整除的數。
二、埃氏篩選法最重要的結論:
N有因數的話,那麼至少有一半的因數不會超過。
舉個例子,要判斷100是不是質數,100 = 10 X 10,只要有1個因數 > ,必然另1個因數 < ,這樣只要判斷10以內有無100的因數即可,使用這種方法的時間複雜度爲O(n*√n)。
可能這樣你還不是很懂,繼續往下看。
三、找到[0,n]範圍內所有素數 的算法基本思路
-
首先將0、1排除:
-
創建從2到n的連續整數列表,[2,3,4,…,n];
-
初始化 p = 2,因爲2是最小的質數;
-
枚舉所有p的倍數(2p,3p,4p,…),標記爲非質數(合數);
-
找到下一個 沒有標記 且 大於p 的數。如果沒有,結束運算;如果有,將該值賦予p,重複步驟4;
-
運算結束後,剩下所有未標記的數都是找到的質數。
可以結合下面這張動圖理解:
四、應用埃氏篩選法優化後的思路
我們發現 [0, N] 範圍內很多 > 的數其實是[0, ]範圍內數的倍數。而 > 且 非[0, ]範圍內數字的倍數的,都是質數。
舉個例子: [0, 100] 範圍內很多 > 的數其實是[0, ]範圍內數的倍數(12,14,16是2的倍數,12,15,18是3的倍數…)。而 > 且 非[0, ]範圍內數字的倍數的(11,13,17…),都是質數。
所以對 標題三 的基本算法思路做出如下優化:
對於步驟4,可以不用從2p開始排除,而是直接從 開始。理由已經在開始講過,所有的小於 的合數都有更小的因數而被排除。
對於步驟5,當 > n 的時候停止計算。
參考鏈接:
《使用埃拉托色尼篩選法在21億內快速查找質數》
《埃拉托色尼篩選法巧解質數問題》
當範圍在int範圍內:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=5000000;
long prime[maxn]; // 存儲一個個確定爲質數的數
bool is_prime[maxn+1]; // 標記範圍內所有數
int p = 0;
int sieve(int n)
{
p = 0;
for(int i=0;i<=n;i++)
is_prime[i]=true; // 所有數先標記爲true
is_prime[0] = is_prime[1] = false; // 把數字0,1標記爲質數
for(int i=2;i<=n;i++)
{
if(is_prime[i]) // 如果這個數沒有被標記爲false
{
prime[p++]=i; // 用prime數組存起來這個數,既存起了質數,又用p表示了質數個數
for(int j=i*i;j<=n;j+=i) // 這裏沒有優化時的寫法是for(int j=2*i; j<=n; j++)。
//因爲小於j(即i^2)內的合數都因爲(根號j)(即i)內有更小的j的的因數而被排除
// 比如3^2 = 9,爲什麼不算2*3 = 6呢, 因爲6<9,所以6因爲3以內有更小的因數而直接被排除
is_prime[j]=false;
}
}
return p; // 返回質數個數
}
int main()
{
int n;
while(~scanf("%d",&n))
{
printf("質數個數是: %d\n",sieve(n));
printf("質數有:\n");
for (int i = 0; i<p; i++)
{
printf("%d ", prime[i]);
printf("\n\n");
}
}
system("pause");
}
當範圍超過了int
static const int N = 1e7;
bool is_prime[N]; // 判斷是否是素數
ll prime[N]; // 存儲素數
ll sieve(ll num)
{
int inx = 0;
for (int i = 0; i<=N; i++)
is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
int MIN = (num > N) ? N : num;
for (ll i = 2; i<=MIN; i++)
{
if (is_prime[i])
{
prime[inx++] = i;
for (ll j = i*i; j<=num; j+=i)
is_prime[j] = false;
}
}
return inx;
}
int gcd(int inx) // 此處由於傳進來都是質數,所以直接相乘即爲gcd
{
int res = 1;
for (int i = 0; i<inx-1; i++)
res *= prime[i];
return res;
}
void C3()
{
ll num; // 輸入數
int p; // 最小公約數
cin >> num;
int inx = sieve(num); // 篩選素數
cout << gcd(inx) << endl;
}