簡單易懂的素數篩法
阿新 • • 發佈:2018-11-11
首先提一句,這是我第一次寫部落格,有不好的地方請在評論處提出來,我會認真改進。
如果你需要求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)。