判斷素數的經典代碼:
int k = sqrt(n); for (int i = 2; i <= k; i++) if (n % i == 0) return false;
乍看合理,實則隱藏**浮點數精度丟失**導致邊界錯誤的風險。本文深入剖析根因,並提供純整數的安全替代實作。
sqrt 返回 `double`,隱式轉換為 `int` 截斷小數部。int n = 2147483647; // 2^31-1,素數 // 若 sqrt 精度不足算出 k = 46339 (實際 √n ≈ 46340.95) // 迴圈只跑到 46339,漏檢 46340?其實 46340^2 > n,但邊界邏輯已破壞。
利用數學恆等式:1 + 3 + 5 + ... + (2k-1) = k²。累加奇數直到和 ≥ n,項數即為 ⌈√n⌉。
int isqrt_ceil(int n) {
int sum = 0, odd = 1, k = 0;
while (sum < n) { sum += odd; odd += 2; k++; }
return k; // 返回最小的 k 使得 k*k >= n
}
bool is_prime(int n) {
if (n <= 3) return n > 1;
if (n % 2 == 0) return false;
int k = isqrt_ceil(n);
for (int i = 3; i <= k; i += 2)
if (n % i == 0) return false;
return true;
}
| 方案 | 精度 | 效能 | 適用範圍 |
|---|---|---|---|
sqrt + double | 大數失效 | 極快 (硬體指令) | n < 2^53 安全 |
| 整數二分法 √n | 精確 | O(log n) | 任意整數 |
| 牛頓迭代法 | 精確 | 極快收斂 | 任意整數/大整數 |
| 奇數平方和 | 精確 | O(√n) (教學用) | 中小整數、教學演示 |
細節決定可靠性。拒絕「似是而非」的 `k = sqrt(n)`,擁抱精確的整數邊界計算,寫出經得起邊界測試的代碼。