小於等於n的素數的個數(埃式篩選法和尤拉篩選)
阿新 • • 發佈:2019-01-31
問題描述
給定數字n,求出小於等於n的素數的個數,假設n<=1000000
思路
找出數字n之前的所有素數,用陣列isprime[i]表示i是否是素數;用num[i]表示小於等於數字i的素數有多少。
那麼最重要的就是解決判斷一個數是否是素數
方法1
最基礎的方法是對每個數從2開始遍歷到根號n,判斷有沒有n的因子,但是顯然效率非常低
方法2
埃式篩選法( Eratosthenes篩選)預處理一下
原理:一個合數總是可以分解成若干質數的乘積
初始化isprime陣列為1,遍歷所有小於題目給定的1000000的數,從1開始判斷,如果改數不是素數,那麼就將其isprime改成0.
顯然1不是素數,num[1]=0;從2開始,如果一個數i是素數(比如2),那麼這個數的倍數都不是素數(也就是,4,6,8,10…)這些數至少有一個素數因子了,因此將i的所有倍數的isprime都設為0,而num[i]=num[i-1]+1;
如果一個數i不是素數,那麼不用處理,直接num[i]=num[i-1]
程式碼如下
const int cont = 1000002;
bool isprime[1000002];
int num[1000002];
int main()
{
int n;
memset(isprime,1,sizeof(isprime));
memset(num,0,sizeof(num));
isprime[1]=0;
num[1]=0;
for(int i = 2;i<cont;i++)
{
if(isprime[i])//是素數
{
num[i]=num[i-1 ]+1;
for(int j = 2*i;j<cont;j+=i)//去掉所有倍數
isprime[j]=0;
}
else
num[i]=num[i-1];
}
while(~scanf("%d",&n))
{
printf("%d\n",num[n]);
}
}
分析:
時間複雜度是O(nloglogn)
效率已經比較高,但是做了一些無用功,一個數會被重複篩選很多遍,
比如2和3的倍數中都有6
方法2
尤拉篩選
尤拉篩選的目的就是要不做重複功,篩選過的不再重複篩選,這裡的prime[i]=k表示的是第i個素數是k;第一重迴圈是找素數,第二重還是藉助找到的素數去除該數的倍數,只不過去除的方式不同
程式碼如下:
const int cont = 1000002;;
int Prime[cont];
bool vis[cont];
void prepare()
{
int num = 0;
memset(vis,true,sizeof(vis));
for(int i = 2; i <= cont; ++i)
{
if(vis[i])
Prime[++num] = i;
for(int j = 1; j <= num; ++j)
{
if (i * Prime[j] > cont)
break;
vis[i * Prime[j]] = false;
if (i % Prime[j] == 0) //表明這個數已經被篩過了
break;
}
}
}