程序設計--素數問題

素數–一個大於1的自然數,除了1和它本身外,不能整除以其他自然數,因其特殊的性質,被廣泛用於密碼學領域,在程序設計競賽及各大公司的面試中也經常出現,今天和大家分享幾道有關素數的基礎問題。

素性測試

問題描述:給定整數num,判斷num是不是素數。

由素數的定義,我們知道一個素數只能擁有兩個約數,即1和它本身,又因爲一個數的約數不能大於它本身,由此只要我們只要遍歷2到num-1,找到除此之外的約數即可判斷其不爲素數,否則就判斷它是一個素數。這樣遍歷時間複雜度爲O(n)。但是,約數是相互對稱的,即n1是num的約數,則num/n1也是num的約數,這樣我們其實搜索2到sqrt(num)(定義sqrt(num)爲num開根號)即可。這樣時間複雜度爲O(sqrt(n))。
代碼:

#include<iostream>

bool is_prime(int num){
    bool result = true;
    if (num<2)
    {
        result = false;
    }
    for (int i = 2; i*i <= num; i++)
    {
        if (num % i == 0)
        {
            result = false;
            break;
        }
    }
    return result;
}

int main(){
    int num = 0;
    printf("請輸入數字:\n");
    scanf_s("%d", &num);
    if (is_prime(num))
        printf("Yes\n");
    else
        printf("No\n");
    system("pause");
    return 0;
}

費馬檢測

這同樣是一個判斷素數的方法,費馬檢測是基於費馬小定理的檢測方法,是一種基於概率的檢測方法。

費馬小定理:
假如p是質數,且Gcd(a,p)=1,那麼 a^(p-1) ≡1(mod p)。即:假如a是整數,p是質數,且a,p互質(即兩者只有一個公約數1),那麼a的(p-1)次方除以p的餘數恆等於1,即a^(p-1) % p = 1。

其算法的基本思想爲:
如果p爲奇數,隨機的在2到p-1之間選擇一個數進行檢測,若不滿足上式,則p一定不是素數;若滿足上式,則p有可能是素數。p爲偶數時,除2之外均不是素數。

代碼:

#include<iostream>

/*
採用反覆平方法來計算 a^(p-1) mod p
*/
int cal_fermat(int a, int p){
    int n = p-1;
    int result = 1;
    int base = a;
    while(n>0){
        if (n&1)
        {
            result = result*base%p;
        }       
        base= base*base%p;
        n>>=1;
    }
    return result;
}

/*
費馬測試
*/
bool prime_fermat(int num){
    if (num == 2){
        return true;    
    }
    if (num < 2){
        return false;
    }
    if (!(num % 2)){
        return false;
    }

    int test = 0;
    while (test < 2){
        test = rand()%num;
    }
    return 1==cal_fermat(test, num);
}

int main(){
    int num = 0;
    printf("請輸入數字:\n");
    scanf_s("%d", &num);
    if (prime_fermat(num))
        printf("Yes\n");
    else
        printf("No\n");
    system("pause");
    return 0;
}

對於大部分a小於p,該算法是正確的,費馬檢測是一個相對可靠的算法,而且在實現大數判斷素數時可以提供相對高的效率來工作。但是該算法並不是嚴格正確的,事實上存在着這樣一些數,它可以滿足所有的a值但卻不是一個素數,這樣的數我們稱爲Carmichael數。

埃式篩法

問題描述:給定整數n,請問n以內有多少個素數

首先,我們來遍歷2到n的所用整數,並使用布爾類型的數組prime_bool來保存每一個數所對應的位置是否爲素數(在初始化時我們將數組中2到n定義爲true);接下來當我們訪問到一個素數時,將其記錄下來,並把其在n範圍內的倍數全部置爲非素數(一個素數不可能含有除1和它本身之外的約數)。
代碼:

#include<iostream>
#define MAX_N 10000

int prime_num[MAX_N];//記錄素數
bool prime_bool[MAX_N+1];

int prime_count(const int end){
    for (int i = 2; i <= end; i++)
    {
        prime_bool[i] = true;
    }
    int count = 0;
    for (int i = 2; i <= end; i++)
    {
        if (prime_bool[i])
        {
            prime_num[count++] = i;
            for (int j = i+i; j <= end; j+=i)
            {
                prime_bool[j] = false;
            }
        }
    }
    return count;
}

int main(){
    int num = 0;
    printf("請輸入數字:\n");
    scanf_s("%d", &num);
    int c = prime_count(num);
    printf("共有%d個素數\n", c);
    printf("所有的素數:\n");
    for (int i = 0; i < MAX_N; i++)
    {
        if (prime_num[i] == 0)
            break;
        if (i % 8 == 0)
            printf("\n");
        printf("%-8d ", prime_num[i]);
    }
    printf("\n");
    system("pause");
    return 0;
}

區間篩法

問題描述:給定區間[l, r],問其中含有多少素數

我們知道,若要判斷r是否爲素數,只需遍歷2到sqrt(r)即可,這樣我們只需在區間[2, sqrt(r)]上進行遍歷,通過埃式篩法將[l, r]範圍內的非素數劃去即可得到[l, r]範圍內的素數。
代碼:

#include<iostream>
#define MAX_N 10000

int prime_num[MAX_N];//記錄素數
bool prime_bool[MAX_N+1];

int prime_segment(int l, int r){

    int count = 0;
    for (int i = 2; i*i <= r; i++)
    {
        prime_bool[i] = true;
    }
    for (int i = l; i <= r; i++)
    {
        prime_bool[i] = true;
    }
    for (int i = 2; i*i <= r; i++)
    {
        if (prime_bool[i])
        {
            for (int j = i+i; j <= r; j += i)
            {
                prime_bool[j] = false;
            }
        }
    }
    for (int i = l; i <= r; i++)
    {
        if (prime_bool[i])
        {
            prime_num[count++] = i;
        }
    }
    return count;
}

int main(){
    int l = 0;
    printf("請輸入開始數字:\n");
    scanf_s("%d", &l);
    int r = 0;
    printf("請輸入結束數字:\n");
    scanf_s("%d", &r);
    int c = prime_segment(l, r);
    printf("共有%d個素數\n", c);
    printf("所有的素數:\n");
    for (int i = 0; i < MAX_N; i++)
    {
        if (prime_num[i] == 0)
            break;
        if (i % 8 == 0)
            printf("\n");
        printf("%-8d ", prime_num[i]);
    }
    printf("\n");
    system("pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章