1. 程式人生 > >關於埃氏篩法詳解

關於埃氏篩法詳解

那天的ppt講的不是很清楚  下來後好多同學都說沒聽懂(。。。。。)

我再補充一下

首先  關於原理

ppt上講的很清楚了  這個原理是相對簡單的  很好理解

如果原理你都理解了 那麼你直接去後邊看用法

如果 你還沒有理解  那麼  我再舉個例子

最樸素的判斷素數方法 是這樣的 對吧

那麼 現在我讓你判斷1-20 之內的素數   你會怎麼做

從1 遍歷到20   挨個判斷一下是不是素數 對吧 

那麼我們來分析一下複雜度  如果不理解什麼是複雜度  你就認為我們算一下迴圈次數好了

從1-20 需要遍歷20次  對於每個數 極端情況下 需要sqrt(n)次才完成能判斷

這一共是多少次呢  sqrt(2)+sqrt(3)+sqrt(4)+sqrt(5)+.........sqrt(20) 次 對嗎 

通過計算器  計算得到 答案是60   也就是需要60次

那麼我們再來看下 我們使用埃氏篩需要幾次

首先 外面的迴圈  毋庸置疑 從2-20遍歷  需要20次(其實是19  我們忽略 進為20)

我們來看裡面的操作

從2開始 2是素數  那麼4不是 6不是  8也不是 10、12、14、16、18、20 都不是  這是9次操作

然後3開始 6 9 12 15 18 都不是素數  這是5次操作

然後4 跳過 5 跳過 6 跳過  

從7開始 14  不是素數  這是1次操作 

後邊的 8 9 10 11 12  14 15 16 18 20  都會跳過  

而11 13 17 19 都不會操作   因為他們的倍數 大於20了

那麼一共進行了20+9+5+1=35次操作   比那個60少了將近一半

其實因為我舉的例子很小  只到20  所以你看到的才快了這麼點

你可以想一下  如果數字變得很大的時候  比如一千萬 你用普通的篩法 是需要更多次數的

所以 這個埃氏篩法快的其實不止這麼一點--(至於究竟快多少 先不管了 你只要知道很快就好了)

 

下面說下   怎麼用它

明白了 原理  用起來就很靈活了

比如

問題一     求1-20之間的素數個數

int main(){
    int cnt=0;//用來計數
    
    bool isprime[21];
    for(int i=0;i<=20;i++)isprime[i]=true;//先全部置為真

    isprime[0]=isprime[1]=false;//1 0 不是素數

    for(int i=2;i<=20;i++){//從2開始往後篩
        if(isprime[i]){
            for(int j=2*i;j<=20;j+=i){
                isprime[j]=false;
            }
        }
        if(isprime[i]){
            cnt++;//如果是素數 就計數
        }
    }
    printf("%d",cnt);
}

問題二

就是ppt上的題目

#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn=1e6+7;//總的範圍規定在這裡
using namespace std;

//我們將這個埃氏篩法寫成一個函式
bool isprime[maxn];
void sieve(){
    for(int i=0;i<=maxn;i++)isprime[i]=true;
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=maxn;i++){//從2開始往後篩
        if(isprime[i]){
            for(int j=2*i;j<=maxn;j+=i){
                isprime[j]=false;
            }
        }
    }
}

int l,r; 
int main(){
    //我們在程式剛開始 先呼叫這個函式
    //把這個isprime陣列處理成我們想要的樣子 用來判斷素數
    //這就是預處理的思想 我們在開頭處理這一次
    //把isprime陣列 裡面 下標是素數的全部變成了true
    //後邊想判斷是不是素數 直接用isprime[i]是不是真就好了
    sieve();
    
    
    int cnt=0;//計數
    scanf("%d%d",&l&r);//輸入 l和r
    for(int i=l;i<=r;i++){//遍歷 l到r  判斷就行了
        if(isprime[i]){
            cnt++;
        }
    }
    printf("%d",cnt);
}

問題三 輸入一個數n   判斷他是不是素數(多組測試資料)

n的範圍是 (n<1e6)

 

這個題很有意思  題上說有多組測試資料(沒說幾組)  每次給你一個n

那麼其實意思就是說 有很多個n讓你判斷 他們是不是素數

而且n最大是1e6

那麼你們想一下

如果我第一個數給你1000000   你用普通的判斷素數的方法  需要sqrt(n)次才能判斷出來

也就是3000次才能判斷出來是不是素數  對吧

3000次也不多  那如果我輸入一共一萬組呢   就需要3000*10000次了吧

所以 這個時候 就體現了 預處理的重要性

我們先預處理出來 1e6以內的所有素數  這樣不管你輸入啥  我直接去看 是不是素數就好了

對吧

那麼 預處理 有些同學也想到了 但是他用最普通的篩法去做預處理

那麼你想一下 1e6每個你都需要去判斷一下是不是素數  我剛才在上邊算了 20以內的素數需要60次才能判斷出來

那1e6以內  需要多少次呢  我告訴你 大概需要1e9次(也就是十億次)

而用埃氏篩法需要幾次呢  可能你們現在還不會算 我直接告訴你們 需要70萬次

十億 和 七十萬  差別不用我說了吧    這就是這個演算法的效率  也是演算法的魅力所在。

#include<stdio.h>
#include<algorithm>
#include<math.h>
const int maxn=1e6+7;//總的範圍規定在這裡
using namespace std;

//我們將這個埃氏篩法寫成一個函式
bool isprime[maxn];
void sieve(){
    for(int i=0;i<=maxn;i++)isprime[i]=true;
    isprime[0]=isprime[1]=false;
    for(int i=2;i<=maxn;i++){//從2開始往後篩
        if(isprime[i]){
            for(int j=2*i;j<=maxn;j+=i){
                isprime[j]=false;
            }
        }
    }
}

int n; 
int main(){
    sieve();//預處理
    
    //輸入 n
    while(scanf("%d",&n)!=EOF){
        if(isprime[n]){
            printf("Yes\n");
        }
        else{
            printf("No\n");
        }
    }
}