1. 程式人生 > >求素數個數(埃氏篩法和尤拉篩法)

求素數個數(埃氏篩法和尤拉篩法)

求1——n的素數的個數,有以下三種方法:

普通的O(\sqrt{n})演算法:

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

bool isprime(int x)
{
    if(x<=1)
        return false;
    for(int i=2;i<=sqrt(x+0.5);++i)//+0.5是為了防止精度誤差
        if(x%i==0)   return false;
        
    return true;
}

一般來說上面這個已經蠻不錯了,可是當n特別大,10^9以上時就有點力不從心了。我們有了下面的埃氏篩法。

埃氏篩法

複雜度:O(loglogn)

埃氏篩法的核心思想就是當我們找到一個素數,那麼這個數小於n的所有倍數肯定都不是素數,同時進行標記。

bool prime[maxn];//prime[i]為true表示i為質數
void init()
{
    for(int i=2;i<maxn;i++)//初始化
        prime[i]=true;
    
    for(int i=2;i<maxn;++i)
    {
        if(prime[i])
            for(int j=2*i;j<maxn;j+=i)//把所有i的倍數都進行標記
            {
                prime[j]=false;
            }
    }
}

尤拉篩法

複雜度O(n)

尤拉篩法優化的一點就是改進了埃氏篩法的一點冗餘:可以發現,在埃氏篩法中,我們對每一個n都標記了不止一次。比如10,當i=2時,10作為2的倍數被標記一次,當i=5時,10依然是5的倍數,又被多餘的標記一次。

尤拉篩法思想:

其基礎是  “任何一個合數都可以由兩個質數相乘得到”  。那麼對於每一個n我們都可以用比它小的某一個質數來標記。

     bool isprime[maxn];//isprime
     int primelistl[maxn];//primelist
     int n;
     int cnt=0;
     scanf("%d",&n);
     for(int i=1;i<=n;++i)
          isprime[i]=true;
     for(int i=2;i<=n;++i)
     {
          if(isprime[i])
          {
               cnt++;    //找到一個素數+1
               primelist[cnt]=i;   //把這個素數新增到素數表裡
          }
          for(int j=1;j<=cnt;++j)  //去列舉所有可以到達的質數
          {
               if(i*primelist[j]>n)  //如果已經大於n則break
                    break;
               isprime[i*primelist[j]]=false;
               if(i%primelist[j]==0)   //找到的是最小的質因子
                    break;
          }
     }
     printf("%d\n",cnt);