大於 1 的自然數,除 1 和自身外無其他因數。最小質數 2 (唯一偶質數),其餘皆為奇數。質數無限多。
public static void printPrimeBasic(int n) {
for (int i = 2; i < n; i++) {
int count = 0;
for (int j = 2; j <= i; j++) {
if (i % j == 0) count++;
if (count > 1) break; // 提前退出
}
if (count == 1) System.out.print(i + " ");
}
}
最直觀,效率最低,n=100000 耗時秒級。
public static boolean isPrimeSqrt(int num) {
if (num < 2) return false;
for (int i = 2; i * i <= num; i++) { // i <= Math.sqrt(num)
if (num % i == 0) return false;
}
return true;
}
public static void printPrimeSqrt(int n) {
for (int i = 2; i < n; i++)
if (isPrimeSqrt(i)) System.out.print(i + " ");
}
利用「若有因數,必有一個 ≤ √n」原理,大幅減少除法次數。n=100000 耗時 ~100ms。
核心思想:**用已找到的質數去篩除合數**,避免重複除法。
public static List sieveEratosthenes(int n) {
boolean[] isComposite = new boolean[n + 1];
List primes = new ArrayList<>();
for (int i = 2; i <= n; i++) {
if (!isComposite[i]) {
primes.add(i);
if ((long)i * i <= n) // 防溢出
for (int j = i * i; j <= n; j += i)
isComposite[j] = true; // 從 i*i 開始標記
}
}
return primes;
}
public static List sieveLinear(int n) {
int[] lp = new int[n + 1]; // 最小質因數
List primes = new ArrayList<>();
for (int i = 2; i <= n; i++) {
if (lp[i] == 0) { lp[i] = i; primes.add(i); }
for (int p : primes) {
if (p > lp[i] || (long)i * p > n) break;
lp[i * p] = p; // 每個合數僅被最小質因數標記一次
}
}
return primes;
}
// 定義法 : ~45 秒 (不可用)
// 平方根優化 : ~1.2 秒
// 埃氏篩 : ~30 ms
// 線性篩 : ~25 ms (最快,且得出最小質因數表)
Miller-Rabin 概率測試。boolean[] 可換為 BitSet 或 long[] 位操作,內存降低 8 倍。從暴力到線性篩,質數算法展現了「利用已知信息減少重複計算」的算法美學。**業務中直接用線性篩模板**,既快又能得 lp 數組,一舉多得。