素數判定方法,方法持續更新...


  素數定義:質數定義為在大於1的自然數中,除了1和它本身以外不再有其他因數。

  方法一(暴力法):素數問題變化莫測,但萬變不離其宗。素數問題最核心的就是如何判斷一個數是否是素數。對於判斷一個數m是否是素數,最原始的方法就是按照素數的定義,試除2開始到m-1的整數,如果無一例外地都不能整除,則該數一定是素數。實現程序如下:

//============================
//判斷是否是素數1
//============================
#include <iostream>
using namespace std;

int main() {
    cout << "please inpout a number.";
    int m;
    cin >> m;
    for (int i = 2; i < m; ++i)
        if (m%i == 0) {
            cout << m << " isn't a prime\n";
            return 1;
        }
    cout << m << " is a prime\n";
    return 0;
}

  方法二(暴力法改進):我們知道如果一個數有因子的話,那么在它的平方根數以內就應該有,否則就沒有因子。例如66的平方根在8與9之間,因為66不是素數,,則它一定有比8還小的因子,我們知道66的因子是2、3、6等。

  現在我們就可以將m試除2到√m的整數,如果無一例外地都不能整除,則該數一定是素數。實現程序如下:
//============================
//判斷是否是素數法2
//============================
#include <iostream>
using namespace std;
int main() {
    cout << "please inpout a number.";
    int m;
    cin >> m;
    double sqrtm = sqrt(m*1.0);
    for (int i = 2; i <= sqrtm; ++i)
        if (m%i == 0) {
            cout << m << " isn't a prime\n";
            return 1;
        }
    cout << m << " is a prime\n";
    return 0;
}

  方法三(暴力法進一步改進):現在舉個例子,判斷102是否是素數,本來要從2試除到10。但事實上,中間的4、6、8、10也都無須試,只需要試除2、3、5、7。直接來說,就是只需要試除2到√m之間的所有素數即可。而所有素數(除了2和3)都滿足6*i-1或6*i+1(i=1、2、3...)。那么代碼又可以改進,如下:

//============================
//判斷是否是素數法3
//============================
#include <iostream>
using namespace std;
int main() {
    cout << "please inpout a number.";
    int m;
    cin >> m;
    //兩個較小數另外處理
    if (m == 2 || m == 3)
        return 1;
    double sqrtm = sqrt(m*1.0);
    for (int i = 5; i <= sqrtm; i += 6)
        if (m %i == 0 || m % (i + 2) == 0)
            cout << m << " isn't a prime\n";
    cout << m << " is a prime\n";
    return 0;
}

  下面這種方法也是本人借鑒別人的,如有侵權請聯系我刪除。

   方法四(篩選法):素數有2、3、5、7、11、13、17、19、23、29...,觀察可知:素數一定在6的倍數的左右,但6的倍數的左右不一定是素數,如23是素數,但25不是素數。則我們可以先通過這個條件將可能是素數的數篩選出來,然后采用方法三,代碼如下:
//============================
//判斷是否是素數法4
//============================
#include <iostream>
using namespace std;
int main() {
    cout << "please inpout a number.";
    int m;
    cin >> m;
    //兩個較小數另外處理
    if (m == 2 || m == 3)
        return 1;
    //不在6的倍數兩側的一定不是質數
    if (m % 6 != 1 && m % 6 != 5) {
        cout << m << " isn't a prime\n";
        return 0;
    }

    double sqrtm = sqrt(m*1.0);
    //在6的倍數兩側的也可能不是質數
    for (int i = 5; i <= sqrtm; i += 6)
        if (m %i == 0 || m % (i + 2) == 0)
            cout << m << " isn't a prime\n";
    //排除所有,剩余的是質數
    cout << m << " is a prime\n";
    return 0;
}

現在對這四種方法的效率進行測試,測試代碼如下:

#include <iostream>
#include <ctime>
using namespace std;

int isPrime_1(int num);
int isPrime_2(int num);
int isPrime_3(int num);
int isPrime_4(int num);
int main() {
    int num = 30000;
    int tstart, tstop; //分別記錄起始和結束時間

                       //測試第一個判斷質數函數
    tstart = clock();
    for (int i = 1; i <= num; i++)
        isPrime_1(i);
    tstop = clock();
    cout << "isPrime_1方法的時間(ms):" << tstop - tstart << endl;

    //測試第二個判斷質數函數
    tstart = clock();
    for (int i = 1; i <= num; i++)
        isPrime_2(i);
    tstop = clock();
    cout << "isPrime_2方法的時間(ms):" << tstop - tstart << endl;

    //測試第三個判斷質數函數
    tstart = clock();
    for (int i = 1; i <= num; i++)
        isPrime_3(i);
    tstop = clock();
    cout << "isPrime_3方法的時間(ms):" << tstop - tstart << endl;

    //測試第四個判斷質數函數
    tstart = clock();
    for (int i = 1; i <= num; i++)
        isPrime_4(i);
    tstop = clock();
    cout << "isPrime_4方法的時間(ms):" << tstop - tstart << endl;
    cout << endl;
    return 0;
}

int isPrime_1(int num) {
    for (int i = 2; i <= num - 1; i++)
        if (num %i == 0)
            return 0;
    return 1;
}

int isPrime_2(int num) {
    double sqrtnum = sqrt(num*1.0);
    for (int i = 2; i <= sqrtnum; i++)
        if (num %i == 0)
            return 0;
    return 1;
}

int isPrime_3(int num) {
    //兩個較小數另外處理
    if (num == 2 || num == 3)
        return 1;
    double sqrtnum = sqrt(num*1.0);
    for (int i = 5; i <= sqrtnum; i += 6)
        if (num %i == 0 || num % (i + 2) == 0)
            return 0;
    return 1;
}

int isPrime_4(int num) {
    //兩個較小數另外處理
    if (num == 2 || num == 3)
        return 1;
    //不在6的倍數兩側的一定不是質數
    if (num % 6 != 1 && num % 6 != 5)
        return 0;
    double sqrtnum = sqrt(num*1.0);
    //在6的倍數兩側的也可能不是質數
    for (int i = 5; i <= sqrtnum; i += 6)
        if (num %i == 0 || num % (i + 2) == 0)
            return 0;
    //排除所有,剩余的是質數
    return 1;
}

判斷1-30000之間素數的耗時:

現在測試判斷1-300000之間素數的耗時:
方法二和方法三的效率之間相差其實不大,什么原因大家可以思考思考。
   方法五(厄拉多塞篩法):如果我們在進行順序遍歷時,每取得一個數(排除0、1),如果將它所有的倍數(排除0、1、本身)都清除,那么,剩下的數是不是必為素數?
  沒錯,這個有趣且實用的方法便是著名的厄拉多塞篩法!

  對此,我們可以聲明一個長度為最大限制數的布爾數組。用布爾值來區別篩選出的數和質數。運用厄拉多塞篩法得代碼如下:

int countPrimes(int n) {
    int count = 0;
    //初始默認所有數為質數
    vector<bool> signs(n, true);
    for (int i = 2; i < n; i++) {
        if (signs[i]) {
            count++;
            for (int j = i + i; j < n; j += i) {
                //排除不是質數的數
                signs[j] = false;
            }
        }
    }
    return count;
}

鏈接:https://leetcode-cn.com/problems/count-primes/solution/ji-shu-zhi-shu-bao-li-fa-ji-you-hua-shai-fa-ji-you/
來源:力扣(LeetCode)
   方法六:本人又學到一種以時間換空間的素數判斷方法,現在先將點預備知識,C++的 bitset 在 bitset 頭文件中,位集bitset是一種關聯容器,因為位集編程簡單,效率也不錯。bitset中只有0、1,且每個元素占一位。用法:
   構造函數
  bitset常用構造函數有四種,如下:
bitset<4> bitset1;  //無參構造,長度為4,默認每一位為0

bitset<8> bitset2(12);  //長度為8,二進制保存,前面用0補充

string s = "100101";
bitset<10> bitset3(s);  //長度為10,前面用0補充
    
char s2[] = "10101";
bitset<13> bitset4(s2);  //長度為13,前面用0補充

cout << bitset1 << endl;  //0000
cout << bitset2 << endl;  //00001100
cout << bitset3 << endl;  //0000100101
cout << bitset4 << endl;  //0000000010101

  可用函數

bitset<8> foo ("10011011");

cout << foo.count() << endl;  //5  (count函數用來求bitset中1的位數,foo中共有5個1
cout << foo.size() << endl;   //8  (size函數用來求bitset的大小,一共有8位
cout << foo.test(0) << endl;  //true  (test函數用來查下標處的元素是0還是1,並返回false或true,此處foo[0]為1,返回true
cout << foo.test(2) << endl;  //false  (同理,foo[2]為0,返回false

cout << foo.any() << endl;  //true  (any函數檢查bitset中是否有1
cout << foo.none() << endl;  //false  (none函數檢查bitset中是否沒有1
cout << foo.all() << endl;  //false  (all函數檢查bitset中是全部為1

  現在言歸正傳來講如何判斷素數,加入要我們判斷2到1億之間有多少素數,首先我們可以調用上面的方法1億次,到當你這樣干的時候,估計你電腦要運行幾十分鍾才能有結果。現在我們就采用空間換時間的方法。設置一個空間為1億的bitset來標記哪些是素數,因為我們知道如果一個數不是素數,那么它的倍數也肯定不是素數。借助這個思想,我們有如下程序:

//=====================================
//利用bitset判斷2到1億之間的素數個數
//=====================================
#include<iostream>
#include<bitset>
using namespace std;

int main() {
    bitset<100000000> *p=new bitset<100000000>;
    p->set();    //每個元素置1
    for (int i = 2; i <= 10000; ++i)
        if (p->test(i))    //第i位為0返回false,為1返回true;
            for (int j = i*i; j < p->size(); j += i)
                p->reset(j);    //每個元素置0
    int num = 0;
    for (int i = 2; i < 100000000; ++i)
        if (p->test(i))
            num++;
    cout << num << endl;
    delete[] p;
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM