質數篩選是數論基礎運算。本文基於埃拉托斯特尼篩法,提供生產級 C++ 實現,分析時間空間複雜度,並給出位圖壓縮、輪因子優化、段式篩法三大進階方向,滿足從嵌入式小規模到服務端大規模 (10⁹+) 的不同需求。
從 2 開始,將每個質數的所有倍數標記為合數。未被標記者即為質數。
#include
#include
#include
void sieve_classic(int max) {
if (max < 2) return;
std::vector is_composite(max + 1, false); // false = 可能是質數
is_composite[0] = is_composite[1] = true;
int limit = static_cast(std::sqrt(max));
for (int i = 2; i <= limit; i++) {
if (!is_composite[i]) {
// 從 i*i 開始,步長 i
for (int j = i * i; j <= max; j += i) {
is_composite[j] = true;
}
}
}
// 輸出
for (int i = 2, cnt = 0; i <= max; i++) {
if (!is_composite[i]) {
printf("%4d%c", i, (++cnt % 10 == 0) ? '\n' : ' ');
}
}
}
| 優化 | 原理 | 效果 |
|---|---|---|
| 外層循環至 √n | 若 n = a×b,至少一因子 ≤ √n | 外層迭代從 n 降至 √n |
| 內層從 i² 開始 | i 以下的倍數已被更小質數標記 | 減少重複標記操作 |
| 步長 2i (僅奇數) | 除 2 外質數皆奇數,偶數倍數必為偶數 | 內層迭代減半 |
| vector<bool> 位圖壓縮 | C++ 特化每個 bool 佔 1 位 | 內存降低 8 倍 (10⁸ 僅 12.5 MB) |
void sieve_optimized(int max) {
if (max < 2) { printf("2\n"); return; }
int half = (max - 1) / 2; // 只存 3,5,7... 索引 k 對應數 2k+3
std::vector is_comp(half, false);
int limit = (static_cast(std::sqrt(max)) - 3) / 2;
for (int i = 0; i <= limit; i++) {
if (!is_comp[i]) {
int p = 2 * i + 3; // 真實質數
// p*p 對應索引: (p*p - 3)/2 = 2i² + 6i + 3
for (int j = 2*i*i + 6*i + 3; j < half; j += p) {
is_comp[j] = true;
}
}
}
printf("2 ");
for (int i = 0, cnt = 1; i < half; i++) {
if (!is_comp[i]) printf("%4d%c", 2*i+3, (++cnt % 10 == 0) ? '\n' : ' ');
}
}
將 [2, n] 分塊 (如 32KB~1MB,適應 L1/L2 緩存),逐塊篩選,僅需儲存基礎質數表 (至 √n) 和當前塊位圖。內存 O(√n + 塊大小),緩存命中率極高,易於多線程並行。
| 場景 | 推薦算法 | 關鍵參數 |
|---|---|---|
| n ≤ 10⁷、單次運行、代碼簡潔優先 | 經典 vector<bool> | max=10⁷ 耗時 ~10ms |
| n ≤ 10⁸、內存受限 (嵌入式) | 奇數位圖優化版 | max=10⁸ 內存 ~6 MB |
| n ≤ 10¹¹、多核服務器、追求極速 | 段式篩法 + 多線程 + 輪因子 (mod 30/210) | primesieve 庫參考實現 |
| 需要單個大數質數測試 (n > 10¹⁵) | Miller-Rabin 確定性版本 | 不需預計算篩表 |
10¹² 內質數計數秒級。埃拉托斯特尼篩法以「標記倍數」的簡單邏輯實現了 O(n log log n) 的近線性複雜度,是算法設計中「空間換時間、冗餘消除、緩存友好」思想的完美體現。根據規模選擇合適優化層級,或直接集成 primesieve,是工程師的理性選擇。