1. 程式人生 > >小於等於n的素數的個數(埃式篩選法和尤拉篩選)

小於等於n的素數的個數(埃式篩選法和尤拉篩選)

問題描述

給定數字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;  
        }  
    }  
}