判斷素數(從暴力篩選,到線性篩選和優化)

【Prime Number】
素數定義:素數就是質數,指出了“1”和他本身,沒有其他因數的數字,
0,1不是素數,第一個素數是2,依次是2,3,5,7,11……
以搜索1000以內的素數爲例,判斷出來的素數存放於一維數組中。
1、一般遍歷(雙重循環)
    採用雙重循環,表層循環遊標 J 負責遍歷2-1000,內層遊標K的範圍是[2,根號J],在內層遊標遍歷的過程中,如果
表層循環遊標J如果能整除K,則說明J代表的數字不是素數,當K遍歷完[2,根號J],仍沒找到因數,說明J是個素數。存入數組。

#include <math.h>
#include <stdio.h>

#define Max 1000    //Max = 1e3 =1 * 10的3次方,即一千
int sushu[Max]={0},count=1;     //保存素數在數組中,count計數素數個數
void prime();

int main(){
    prime();
    int i;
    for(i=1; i<Max; i++){
        if(sushu[i] == 0){
            break;
        }
        printf("%3d ",sushu[i]);
        if(i%10 == 0){
            printf("\n");
        }
    }
    return 0;
}

void prime(){
    int total;      //每次循環等於 sqrt(j),取[ 2,sqrt(j) )區間判斷素數
    int j,k;
    for(j=2; j<Max; j++){
        total=sqrt(j);
        for(k=2; k<=total; k++){
            if(j%k == 0){
                break;
            }
        }
        if(k > total){
            sushu[count++]=j;
        }
    }
}


2、埃氏篩選(未優化的線性篩選)
    我們知道合數(非素數)是可以用素數的倍數表示,有的更可以用兩個合數表示,所以篩選出素數同時,篩掉素數的倍數。篩掉的合數,也可以用來篩掉合數的倍數。
    例如,2是素數,則篩掉4,6等,篩掉了在IsPrime數組置零,表示非素數,下一步3是素數,篩掉6,9等,因爲在2處,4被篩掉了,執行篩選會篩掉8,篩選5,
    5是素數,就有篩掉10,15之類,依次類推,6篩掉了,篩掉12等,下一個7……
    這樣我們篩選出非素數的速度高於篩出素數,保證篩出的都是素數(1000以內素數168個,遠遠少於合數)
    篩選的方法,sushu[]數組負責存儲素數,IsPrime[]數組每一位數組元素,代表數組下標數字是不是素數,0代表不是,1代表是。負責協助存儲素數。
    剛開始假設2-1000全部都是素數,所以IsPrime[]剛開始全部初始化爲0。我們知道這個第一個素數是2,所以就有
    for(j=2; j<Max; j++){
                if(IsPrime[j]){         //IsPrime[j]=1,說明是素數
                        sushu[count++]=j;
                }
                for(k=j*2; k<=Max; k+=j){
                        IsPrime[k]=0;
                }
    }
    J的for循環,提供便利2-1000,if根據IsPrime數組判斷是否爲素數,內層的遊標爲K的for循環,負責篩出素數後繼續篩出他的倍數,如果當前遍歷到的是非素數,
    則篩非素數的倍數,這樣就把大量的非素數篩除出去了。

#include <stdio.h>

#define Max 1000
int sushu[Max]={0},count=1;     //sushu數組用來保存1000以內素數,count作爲下標
int IsPrime[Max];
//int IsPrime[Max]={1};          //IsPrime數組0代表不是素數,1代表是素數
                                //用來協助篩選素數,剛開始認爲所有數字都是素數
void prime();

int main(){
    prime();
    int i=1;
    for(; i<Max; i++){
        if(sushu[i] == 0){
            break;
        }
        printf("%3d ",sushu[i]);
        if(i%10 == 0){
            printf("\n");
        }
    }
    return 0;
}

void prime(){
    memset(IsPrime,1,sizeof(IsPrime));
    IsPrime[0]=IsPrime[1] =0;   //0,1不是素數,第一個素數是2
    int j,k;
    for(j=2; j<Max; j++){
        if(IsPrime[j]){         //IsPrime[j]=1,說明是素數
            sushu[count++]=j;
        }
        for(k=j*2; k<=Max; k+=j){
            IsPrime[k]=0;
        }
    }


}


3、優化後的線性篩選(歐拉篩選)
    主要是優化了像是素數2,3都會篩除6這樣的重複。

#include <stdio.h>

#define Max 1000
int sushu[Max]={0},count=1;     //sushu數組用來保存1000以內素數,count作爲下標
int IsPrime[Max];
//int IsPrime[Max]={1};          //IsPrime數組0代表不是素數,1代表是素數
                                //用來協助篩選素數,剛開始認爲所有數字都是素數
void prime();

int main(){
    prime();
    int i=1;
    for(; i<Max; i++){
        if(sushu[i] == 0){
            break;
        }
        printf("%3d ",sushu[i]);
        if(i%10 == 0){
            printf("\n");
        }
    }
    return 0;
}

void prime(){
    memset(IsPrime,1,sizeof(IsPrime));
    IsPrime[0]=IsPrime[1] =0;       //與上同理
    int j,k;
    for(j=2; j<Max; j++){
        if(IsPrime[j]){
            sushu[count++]=j;
        }
        for(k=1; k<count && sushu[k]*j<Max; k++){
            IsPrime[sushu[k]*j]=0;
        }
    }
}


4、區間篩選
    假設篩選[a,b)區間,所以先篩出[2,根號b)的素數,然後篩出[2,根號a)的素數,兩者減去重疊就好了。不多說了
5、源碼+思考+理解
    數據結構部分會繼續更新,即使環境惡劣

附圖:1000以內的素數表,共168個素數,2開始,997結束。(左圖爲素數表,右圖爲程序運行結構)

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章