篩法
所謂篩法是一種思想,就像名字一樣,篩去多余的,篩去錯誤的。多數情況用數組標記,復雜度看起來很大,但代碼跑起來確是越跑越快。
素數篩法
問題引入
把n以內素數全找出來(n<=100000)
大家一定想得到第一種方法,暴力,遍歷。
//暴力
#include<stdio.h>
#include<math.h>
int isprime(int n)
{
n = sqrt(n);
for(int i = 2; i < n; i++)
{
if(n%i==0)
return 0;
}
return 1;
}
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
if(isprime(n))
printf("%d ",i);
}
return 0;
}
注:用n=sqrt(n),可以增加運算速度,畢竟sqrt也是很慢的。但是這種方法還是很慢,慢在每次都要判斷。
接下來看篩法
代碼分析
所謂素數,就是不能表示為比它更小的數的乘積。
且,任何一個素數的倍數一定不是素數。
我們先定義一個數組來存100000以內數是否的素數,下標表示數,數組的值1表示是素數,0表示不是素數。
我們可以換個角度思考,要找素數,其實只要把不是素數的排除就可以了。
接下來我們可以構造一張表。
初始
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
我們可以把2的倍數變成1,因為任何一個素數的倍數一定不是素數。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
然后是3的倍數
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
。。。。。。
照這樣就可以得到一張素數表了,哈哈。
看代碼。
//篩選法
#include<stdio.h>
#include<math.h>
int a[100001];
int main()
{
int n;
while(scanf("%d",&n) == 1)
{
for(int i = 2; i <= n; i++)
{
if(a[i] == 0)
{
for(int j = i*2; j < n; j+=i)
a[j] = 1;
}
}
printf("2");
for(int i = 3; i <= n; i++)
{
if(a[i] == 0)
printf(" %d",i);
}
}
reutrn 0;
}
這個代碼還可以改進。
第一,我們不用篩到n,其實只要和暴力方法一樣,篩到sqrt(n)就可以了。
第二,我們篩3時多篩了3*2,篩5時多篩了5*2,5*3,5*4。只要從i*i開始篩就可以了。
//篩選法優化一
#include<stdio.h>
#include<math.h>
int a[100001];
int main()
{
int n;
while(scanf("%d",&n) == 1)
{
n = sqrt(n);
for(int i = 2; i <= n; i++)
{
if(a[i] == 0)
{
for(int j = i*i; j < n; j+=i)
a[j] = 1;
}
}
printf("2");
for(int i = 3; i <= n; i++)
{
if(a[i] == 0)
printf(" %d",i);
}
}
reutrn 0;
}
其實還可以改進,這里我就不說了。
把2的情況用位運算,再把j+=i變為j+=2*i
總結
看起來時間復雜度為O(n*n),其實在處理很多數據時,它是越跑越快,相當於O(6*n)。
另外
1,如果要判斷素數個數,可以用一個數組存一下有幾個,結合篩法,也是很方便的。
2,如果要判斷50-100有幾個素數,只要(100的素數個數-50的素數個數),再判斷一下100與50的素數情況。
3,這種篩法叫埃拉托斯特尼篩法。
下一篇:線性篩法(一)--素數篩法(一)