最稳妥的IsPrime实现是:n<2返回false,n==2返回true,n为偶数返回false,再从3开始试除到(int)Math.Sqrt(n)(含)的奇数;因√n已覆盖所有因子配对,用n/2会显著降低性能。

直接说结论:用 Math.Sqrt 配合 % 取余循环,是最常用也最稳妥的判断方式;别用试除到 n-1,更别信“只除奇数就行”这种半吊子优化。
怎么写一个靠谱的 IsPrime 函数
核心逻辑就一条:从 2 试除到 (int)Math.Sqrt(n)(含),中间有任何一个能整除,就不是素数。注意边界处理——1 不是素数,2 是素数,负数和 0 直接返回 false。
实操建议:
-
n < 2立刻返回false(0、1、负数全拦住) -
n == 2直接返回true(唯一偶素数) -
n % 2 == 0返回false(排除其他所有偶数) - 从
3开始,只试除奇数,步长为2,上限取(int)Math.Sqrt(n)(不是n / 2,也不是n - 1)
示例片段:
static bool IsPrime(int n)
{
if (n < 2) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (int i = 3; i <= (int)Math.Sqrt(n); i += 2)
if (n % i == 0) return false;
return true;
}为什么必须用 Math.Sqrt 而不是 n / 2
因为如果 n 有大于 √n 的因子,那它一定对应一个小于 √n 的配对因子。试除到 √n 就覆盖了全部可能性。用 n / 2 会多跑几倍循环,对大数(比如 1000003)性能差距明显。
容易踩的坑:
-
Math.Sqrt返回double,强制转int会向下取整,但不影响正确性(比如√100 = 10.0→10,√99 ≈ 9.95→9,而9已足够) - 忘了加等号
<=,比如i < Math.Sqrt(n)会让25错判成素数(5没被试除) - 用
float或没转int直接参与循环变量,编译不过或隐式转换出错
long 类型怎么办?Math.Sqrt 还好使吗
对 long,Math.Sqrt 接收 double,而 double 只能精确表示最多约 2^53 的整数,超过这个范围(比如 long.MaxValue ≈ 2^63)时,Math.Sqrt 返回值会丢失精度,导致上限算错、漏判。
实操建议:
- 小范围
long(比如 <10^12)仍可用(long)Math.Sqrt(n),问题不大 - 真要处理超大数,改用二分法找整数平方根,或用
BigInteger.IsProbablePrime(需 .NET 5+,且是概率算法) - 更简单粗暴的做法:循环条件写成
i <= n / i,避免开方——n / i >= i等价于i * i <= n,不依赖浮点,也无溢出风险(前提是i不太大)
常见错误现象和调试线索
你写的 IsPrime 返回结果不对?先盯这几个点:
- 输入
4返回true→ 没处理n % 2 == 0的情况,或者循环从2开始但没包含2 - 输入
25返回true→ 上限用了<而不是<=,i没走到5 - 输入
-5报异常或返回true→ 缺少n < 2检查 - 大数(如
1000000007)卡住或超时 → 循环上限写成了n / 2或n - 1
素数判断本身不复杂,但边界和数值类型转换这两个地方,最容易在上线后悄悄翻车。










