1. 程式人生 > >簡單易懂的素數篩法

簡單易懂的素數篩法

首先提一句,這是我第一次寫部落格,有不好的地方請在評論處提出來,我會認真改進。


如果你需要求1~n個數中有幾個素數,你會去怎麼求???


方法一:
最直接想到的是先列舉1~n中每一個數字,再寫一個函式pd(x)來判斷第x個數是否是素數,再輸出。

#include<iostream>
#include<cstdio>
using namespace std;
int n;
void prime (x)
{
    for(int i = 2;i * i <= x;i ++)
        if(! x%i)return ;
    printf
("%d",x); return ; } int main() { scanf("%d",&n); for(int i = 1;i <= n;i ++) prime (i); return 0; }

時間複雜度約為 Θ(nlogn)
大資料會卡住


方法二:
埃氏篩
思想:
1.新設定一個bool變數vis[i]表示第i個數是否被篩除,如果沒有那麼就是素數。
2.每次篩出素數i,把該素數i的所有倍數都篩除。

#include<iostream>
#include<cstdio>

using
namespace std; //num儲存素數,vis表示是否被篩出 int n,num[100005],cnt; bool vis[100005]; int main() { //freopen("test.in","r",stdin); //freopen("test.out","w",stdout); scanf("%d",&n); vis[1] = 1;//1既不是素數,也不是合數 for(int i = 2;i <= n;i ++)// 複雜度Θ(nlogn) { if(vis[i] == 0)//如果沒有被篩出,就是素數
{ num[++cnt] = i; for(int j = (i<<1);j <= n;j +=i)//篩出合數 複雜度Θ(logn) vis[j] = 1; } } for(int i = 1;i <= cnt;i ++) printf("%d ",num[i]); return 0; }

個人感覺時間複雜的能比普通暴力快一點


最優秀的方法:線性篩(尤拉篩)
思想:不好解釋,看圖
這裡寫圖片描述
與埃氏篩相同,定義一個vis[i],表示是否被篩出,沒有被篩出的就是素數。再往下執行具體結合程式碼和圖片理解吧

#include<iostream>
#include<cstdio>

using namespace std;

//num儲存素數,vis表示是否被篩出
int n,num[100005],cnt;
bool vis[100005];

int main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    scanf("%d",&n);
    vis[1] = 1;//========================================以及不是質數也不是合數
    for(int i = 2;i <= n;i ++)//                 複雜度Θ(n)
    {
        if(! vis[i])num[++cnt] = i;//====================如果i沒有被篩出,那麼它就是素數
        for(int j = 1;j <= cnt && num[j]*i <= n;j ++)// 複雜度Θ(1) 
        {
            vis[num[j]*i] = 1;
            if(i % num[j] == 0)break;
        }
    }
    for(int i = 1;i <= cnt;i ++)
        printf("%d ",num[i]);
    return 0;
} 

這個的時間複雜度就很優秀,將近Θ(n)。