一般篩法求素數

一般篩法求素數

標籤: PAT


下面是一個模板代碼,在PAT考試中能夠流暢默寫會大大加速我們的編程速度,一般篩法雖然在速度上不如線性篩法,但是在PAT考試中,這一點性能可以忽略不計了.另外需要特別注意的是:

PAT打表解法有可能無法提交!
PAT打表解法有可能無法提交!
PAT打表解法有可能無法提交!

重要的事情說三遍!,一定要記住

在這樣的情況下,熟練掌握篩法就是至關重要的了.

下面是沒有優化的代碼,

    bool prime [MAX];
    memset(prime,true,MAX);
    vector<long> v;
    for(int i = 2; i < MAX; i ++){
        if(prime[i] == true){
        v.push_back(i);
            for(int j = i ; j < MAX ; j += i){
                prime[j] = false;
            }
        }
    }

有必要作幾點說明:

  • prime最好使用bool數組,bool雖然邏輯上是佔用一個字節.但是實際上,在定義成一個bool數組後,編譯器有可能會對這個數組做一些優化,使用一個字節的二進制位來代表true或者false.所以在一些數字比較大,對內存的限制比較緊張的情況下,使用bool數組可以有效的節省內存.
  • memset函數的使用,爲了避免因爲平臺不一樣導致奇怪的問題,這裏統一引入memory.h頭文件.
  • 爲了後面輸出等其他操作的方便,存放素數的容器這裏統一使用vector.

下面是一些可以優化的地方

  • 內層循環中,j 從 i開始篩後面的數字,這樣其實是完全不必要的.可以直接讓j 從 i的平方開始往後篩.這樣做的理由是i 到 i平方之間會路過的數字肯定已經被比i小的數字篩過了.例如:質數5如果按照常規方法,要經過5 , 10 , 15 , 20 , 25.將這些數字拆分就會發現依次是1 * 5,2 * 5,3 * 5,4 * 5,5 * 5,而1,2,3,45的乘積在之前肯定已經被篩過.所以可以直接讓j = i * i開始內層循環.
  • 上面這種優化仍然是存在問題的.這會讓j的定義必須變得特別大.因爲當i = 10000的時候,j就必須要達到100000000纔可以容納,這回導致沒有某種類型可以容納它.所以我們讓i的初始值定義爲sqrt(double(MAX) + 0.5),對MAX四捨五入後取根號操作,就可以讓篩法的範圍正好覆蓋MAX的範圍.

即使這樣優化仍然有需要注意的地方
就是將素數放入vector的操作的位置.因爲檢查prime[i]是否爲true的操作現在只進行到根號MAX就停止了.所以原來地方的對V的操作無法訪問所有爲true的prime元素.所以在執行完篩法之後,我們需要再掃描一遍prime數組,將prime爲true的元素單獨放入.

典型例題

  • PAT 1013 數素數:利用素數個數定理x/ln(x)估算出達到目標的素數範圍,使用篩法曬出素數表.即可求解,注意控制格式.
  • PAT 1027 素數對擦想:篩出固定範圍的表後,遍歷減去相鄰的即可求得個數.

下面是最終代碼

    bool prime [MAX];
    memset(prime,true,MAX);
    vector<long> v;
    int m = sqrt(double(MAX) + 0.5);
    for(int i = 2; i < m; i ++){
        if(prime[i] == true){
            for(int j = i * i ; j < MAX ; j += i){
                prime[j] = false;
            }
        }
    }
    for(long i = 2 ; i < MAX;i ++){
        if(prime[i])
            v.push_back(i);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章