1. 程式人生 > >素數檢測演算法

素數檢測演算法

因為1既不是素數也不是合數,所以下面的實現程式碼中不考慮小於2的情況。

  • C語言完整原始碼可以到這裡檢視

  • Java完整原始碼可以點選這裡檢視

本文以C語言進行講解,建議對著完整的原始碼看。

1. 暴力求解

最原始、最粗暴的方法就是從頭到尾逐個進行檢測,一旦遇到可被整除的數馬上返回false

bool is_prime_1(int n) {
    for (int i = 2; i < n; i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true
; }

該演算法時間複雜度為n2

2. sqrt開方

對於素數求解最簡單的優化,就是對n進行開方,減少迴圈次數。

bool is_prime_2(int n) {
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

該演算法時間複雜度為n

3.開方優化

使用乘法替代開方。數學庫中的sqrt開方無非是使用迭代法實現,CPU可能對這些數學運算進行了優化,但與乘法相比sqrt顯得太過耗時。

bool is_prime_3(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

4.優化偶數檢測

很顯然除了2這個偶數是素數,其他所有的偶數都是合數,所以可以對輸入數進行奇偶檢測,優化演算法。

下面的奇偶檢測,使用二進位制位運算進行優化:計算機中的資料以2進位制進行儲存,如果一個整數是偶數,則最後一位肯定是0,(n & 1) == 0即可判斷一個數是否為偶數。計算機位運算顯然比求餘運算速度快。

bool is_prime_4(int n) {
    if (n == 2)
        return true;
    if ((n & 1) == 0)
        return false;
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

該演算法時間複雜度為n2

5.排除已有素數的倍數

從1開始數,每6個數為1組,其中每一組的2、3、4、6的數可以表示成6k+2、6k+3、6k+4、6k+6,很明顯這些數都能被2和3整出,所以我們對2和3進行檢測後,這些數就可以不用檢測了,而可能出現的素數在6k+1和6k+5位置上,這些數並沒有被檢測過,所以需要我們求取檢測(雖然這些檢測仍存在重複)。因為1既不是素數也不是合數,所以我們可以把需要檢測的數標記為6k-1和6k+1,並從5開始檢測。

素數演算法優化

演算法實現如下:

bool is_prime_5(int n) {
    if (n <= 3)
        return true;
    else if ((n & 1) == 0 || n % 3 == 0)
        return false;
    for (int i = 5; i * i <= n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0)
            return false;
    }
    return true;
}

該演算法的時間複雜度為n3

舉一反三

聰明的你也許已經發現,前面說的偶數優化,也是用相同的原理。

素數演算法優化

我們可以根據這個規律更進一步,讓分組大小為2*3*5=30

素數演算法優化

我們只需要對7開始出現的素數進行檢測即可。

bool is_prime_6(int n) {
    if (n <= 3 || n == 5)
        return true;
    else if ((n & 1) == 0 || n % 3 == 0 || n % 5 == 0)
        return false;
    for (int i = 7; i * i <= n; i += 30) {
        if (n % (i - 7 + 7) == 0 || n % (i - 7 + 11) == 0 || n % (i - 7 + 13) == 0
            || n % (i - 7 + 17) == 0 || n % (i - 7 + 19) == 0
            || n % (i - 7 + 23) == 0 || n % (i - 7 + 29) == 0
            || n % (i - 7 + 31) == 0)
            return false;
    }
    return true;
}

測試並比較

void compare_and_test() {
    typedef bool(*PFUNC)(int);
    PFUNC pFunc[] = { is_prime_1, is_prime_2, is_prime_3,
        is_prime_4, is_prime_5, is_prime_6, is_prime_7,is_prime_8 };
    size_t length = sizeof(pFunc) / sizeof(PFUNC);
    size_t count = 0;
    struct timespec start, end;
    for (size_t i = 0; i < length; i++) {
        count = 0;
        timespec_get(&start, TIME_UTC);
        for (int j = 2; j < 100000; j++) {
            if ((*pFunc[i])(j)) {
                count++;
            }
        }
        timespec_get(&end, TIME_UTC);
        double duration = compute_duration(&end, &start);
        printf("%d\n%lf\n", count, duration);
    }
}

執行結果:

9592
1778329300.000000
9592
32828300.000000
9592
14184100.000000
9592
9072200.000000
9592
7018500.000000
9592
6040100.000000