C語言素數判定完整解析!試除法、埃拉托斯特尼篩法與最佳化

1. 前言

在學習 C 語言的過程中,「質數判定」是常出現的基本問題之一。對於程式設計初學者來說,「了解數字的性質,並具備選擇適當演算法的能力」是一個很好的機會。此外,透過學習更有效率的質數判定演算法,亦能加深對演算法計算量差異與程式最佳化的理解。 本文將使用 C 語言介紹質數判定的方法,並提供可實際執行的範例程式碼。從基本的試除法到更快速的埃拉托斯特尼篩(篩法),將介紹各種不同的手法。

2. 什麼是質數?

質數是指除 1 與其本身外沒有正因數的自然數。例如,2、3、5、7、11 等都是質數。

2.1. 質數的基本特徵

  • 最小的質數是 2(唯一的偶數質數)
  • 1 不是質數(因為只有 1 個因數)
  • 質數無限存在
  • 若大於等於 2 的整數 n 不是質數,必定能被大於等於 2 的質數整除

2.2. 質數的具體例子

數值是否為質數
2✅ 質數
3✅ 質數
4❌ 合成數(2×2)
5✅ 質數
6❌ 合成數(2×3)
7✅ 質數
8❌ 合成數(2×4)
9❌ 合成數(3×3)

3. C語言的基本素數判定方法(🔰 初學者向)

在此,我們介紹最基本的方法「試除法」

3.1. 試除法(基本)

試除法是指,對於 2 以上的整數 N檢查是否能被 1 與 N 本身以外的整數整除的方法。
  • 若 N 為 2 以上的整數,確認是否能被 2 到 N-1 的數字整除
  • 若有可整除的數字則為「合成數」,若無則為「素數」
範例程式碼(基本版)
#include <stdio.h>

int is_prime(int n) {
    if (n < 2) return 0;  // 1 以下不是素數
    for (int i = 2; i < n; i++) {  // 確認是否能被 2 到 n-1 整除
        if (n % i == 0) return 0;
    }
    return 1;
}

int main() {
    int num = 7;
    if (is_prime(num))
        printf("%d 是素數\n", num);
    else
        printf("%d 不是素數\n", num);
    return 0;
}
執行結果
7 是素數
此方法簡單且易於理解,但有因計算量為 O(N) 且較大,當 N 增大時處理時間會變長的缺點。接下來的章節將介紹更有效率的方法。

4. C語言中有效判斷質數的方法【埃拉托斯特尼篩法・改良版試除法】

為了最佳化質數判斷的計算,說明以下兩種方法。
  1. √N まで的試除法(最佳化)
  2. 埃拉托斯特尼篩(篩子)

4.1. √N まで的試除法(最佳化)

非質數的數字 N 必定有 √N 以下的因數,因此只要檢查 到 N 的平方根的範圍 即可,這是重點。 範例程式碼
#include <stdio.h>
#include <math.h>

int is_prime_optimized(int n) {
    if (n < 2) return 0;
    if (n % 2 == 0 && n != 2) return 0;  // 偶數除2外皆非質數
    for (int i = 3; i <= sqrt(n); i += 2) {  // 只檢查奇數(跳過偶數)
        if (n % i == 0) return 0;
    }
    return 1;
}

int main() {
    int num = 29;
    if (is_prime_optimized(num))
        printf("%d 是質數n", num);
    else
        printf("%d 不是質數n", num);
    return 0;
}
執行結果
29 是質數
透過此方法,計算量從 O(N) → O(√N) 改善,即使對於大數也能快速判斷。

4.2. 埃拉托斯特尼篩(篩子)

埃拉托斯特尼篩是一種「透過刪除小質數的倍數,來有效找出質數的演算法」。
  • 從 2 開始,刪除其所有倍數
  • 接著刪除剩下的最小數(3)的倍數
  • 如此重複,僅留下質數
範例程式碼
#include <stdio.h>
#include <stdbool.h>

void sieve(int n) {
    bool prime[n + 1];
    for (int i = 0; i <= n; i++) prime[i] = true;

    for (int p = 2; p * p <= n; p++) {
        if (prime[p]) {
            for (int i = p * p; i <= n; i += p)
                prime[i] = false;
        }
    }

    for (int p = 2; p <= n; p++)
        if (prime[p])
            printf("%d ", p);
    printf("n");
}

int main() {
    int n = 50;
    sieve(n);
    return 0;
}
執行結果
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
埃拉托斯特尼篩的計算量為 O(N log log N)在大量求取質數的情況下最為適合

5. C語言的素數判定演算法效能比較【速度測量與最佳化】

素數判定的演算法有幾種不同的手法,但它們的處理速度與效率差異很大。本節將對以下三種方法,測量 C 語言的執行時間,並探討哪種手法在何種情況下有效。
  • 試除法(基本): O(N)
  • 至 √N 的試除法(最佳化): O(√N)
  • 埃拉托斯特尼篩法(在求取範圍內的素數時): O(N log log N)

5.1. 執行時間的測量方法

在 C 語言中,可以使用 clock() 函數來測量處理時間。 基本的執行時間測量範例程式碼
#include <stdio.h>
#include <time.h>

void measure_time(void (*func)(int), int n) {
    clock_t start, end;
    start = clock();  // 開始測量

    func(n);  // 執行測量目標函式

    end = clock();  // 結束測量
    printf("執行時間: %lf 秒\n", (double)(end - start) / CLOCKS_PER_SEC);
}

5.2. 各手法的執行時間測量

5.2.1. 試除法(O(N))

程式碼
int is_prime(int n) {
    if (n < 2) return 0;
    for (int i = 2; i < n; i++) {
        if (n % i == 0) return 0;
    }
    return 1;
}

void test_trial_division(int limit) {
    int count = 0;
    for (int i = 2; i <= limit; i++) {
        if (is_prime(i)) count++;
    }
    printf("總素數: %d\n", count);
}

5.3. 執行時間的比較(圖表化)

方法計算量10,000 時100,000 時1,000,000 時
試除法O(N)0.25 秒2.5 秒數十秒〜數分
√N 試除法O(√N)0.02 秒0.15 秒1.5 秒
埃拉托斯特尼篩法O(N log log N)0.001 秒0.005 秒0.03 秒
可見埃拉托斯特尼篩法壓倒性地快

6. FAQ(常見問題)

在本節中,我們將回答有關 C 語言素數判定的常見問題。

Q1: 為什麼在素數判定中使用平方根?

A: 若某個數 N 不是素數,則必定在 N 的平方根以下存在約數,這是一個數學性質。 例如,當 N = 49 時,約數的配對如下。
  • 1 × 49
  • 7 × 7(平方根)
  • 49 × 1
超過平方根的約數已在前一步檢查過,因此可以將計算量從 O(N) 降低到 O(√N)。

Q2: 在什麼情況下應該使用埃拉托斯特尼篩法?

A: 埃拉托斯特尼篩法在想要快速找出某個範圍內所有素數的情況下最為適合。 ✅ 應使用埃拉托斯特尼篩法的情況
  • 想列出 1 到 100 萬之間的素數
  • 想有效率地對多個數字進行素數判定

Q3: C 語言的環境會影響素數判定的速度嗎?

A: 是的,C 語言的編譯器、最佳化選項與執行環境會大幅影響素數判定的速度。 ✅ 影響因素
  • 編譯器類型(GCC, Clang, MSVC 等)
  • 編譯時的最佳化選項
  • CPU 效能(特別是迴圈處理速度)
💡 加速提示
gcc -O3 prime.c -o prime
這樣編譯器會對迴圈進行最佳化,處理速度可能會提升。

7. 總結與未來的學習方向

在先前的章節中,我們已詳細說明了使用 C 語言的素數判定的基礎到應用

7.1. 文章總結

🔹 什麼是素數?

  • 除 1 與其本身外沒有其他因數的自然數
  • 檢查到平方根即可,減少不必要的計算(計算量 O(√N))

🔹 使用 C 語言的素數判定方法

方法計算量特點
試除法(基本)O(N)適合初學者。簡單但慢
至 √N 的試除法O(√N)適用於單一數字的素數判定
埃拉托斯特尼篩法O(N log log N)適合大量求取素數

🔹 選擇最佳演算法

目的推薦演算法
判斷單一數字是否為素數√N 試除法
在範圍內建立素數清單埃拉托斯特尼篩法
判斷 10⁹ 以上的大數Miller-Rabin 法(概率性)

7.2. 未來的學習方向

1️⃣ 更高階的素數判定演算法

  • Miller-Rabin 素數判定法(概率性)
  • AKS 素數判定法(決定性)

2️⃣ C 語言的最佳化技術

  • 編譯器的最佳化選項(-O2-O3
  • 利用多執行緒的平行處理

3️⃣ C 語言的大數處理

  • 使用 GMP(GNU Multiple Precision)函式庫

7.3. 實作專案建議

專案 1:素數計數器專案 2:素因數分解工具專案 3:素數搜尋應用程式

7.4. 總結

  • 試除法(基本)的計算量為 O(N),較慢,因而不適合大數。
  • √N 試除法的計算量為 O(√N),適合單獨判斷大數。
  • 埃拉托斯特尼篩法的計算量為 O(N log log N),能快速取得素數清單。
  • 依需求選擇最適的演算法非常重要!
侍エンジニア塾