
本文详解为何原代码中内层循环变量 j 始终为 0,并提供健壮、高效的质数筛选实现方案,涵盖逻辑修正、边界优化与可读性提升。
本文详解为何原代码中内层循环变量 `j` 始终为 0,并提供健壮、高效的质数筛选实现方案,涵盖逻辑修正、边界优化与可读性提升。
在原始代码中,j 恒为 0 的根本原因在于:内层 for 循环每次刚执行第一次迭代(j = 0)就必然触发 break,导致循环永远无法继续递增。问题核心出现在以下逻辑分支中:
if (i % j === 0) {
prime.push(i);
break; // ✅ 此处跳出
} else {
notPrime.push(i);
break; // ✅ 此处也跳出
}而更关键的是——当 j = 0 时,i % j 会产生 NaN 或运行时错误(除零异常),实际执行中多数浏览器会抛出 Uncaught RangeError: Invalid array length 或直接中断。即使未崩溃,该判断本身在数学和逻辑上也是无效的:0 不能作为除数,也不应参与质数判定。
✅ 正确的质数判定逻辑
质数定义:大于 1 的自然数,且仅有 1 和它本身两个正因数。因此:
- 0 和 1 不是质数;
- 判定 i 是否为质数,需检查其是否存在 2 到 ⌊i/2⌋(含)之间的真因数;
- 若找到任意一个 j 满足 i % j === 0,则 i 必为合数,可立即 break;
- 若遍历完所有候选 j 均无整除,则 i 是质数。
✅ 优化后的完整实现
const n = 100;
const prime = [];
const notPrime = [];
// 从 2 开始(1 不是质数,0 和负数不考虑)
for (let i = 2; i <= n; i++) {
let isPrime = true;
// 检查 2 到 i/2(向下取整)之间是否有因数
// 注:j <= Math.floor(i / 2) 等价于 j <= i / 2(因 j 为整数)
for (let j = 2; j <= i / 2; j++) {
if (i % j === 0) {
isPrime = false;
break; // 找到因数,确定为合数,提前退出
}
}
// 循环结束后再统一归类
if (isPrime) {
prime.push(i);
} else {
notPrime.push(i);
}
}
console.log('质数:', prime); // [2, 3, 5, 7, 11, ..., 97]
console.log('非质数:', notPrime); // [4, 6, 8, 9, 10, ..., 100]⚠️ 关键注意事项
- 禁止用 var j 声明内层变量:原文使用 var j 会导致变量提升和作用域污染,应统一使用 let j 保证块级作用域;
- 起始值必须为 2:j = 0 导致除零错误;j = 1 恒成立(任何 i % 1 === 0),会使所有数误判为“非质数”;
- 上界优化合理:大于 i/2 的数不可能整除 i(除非 i 自身,但 i 不在检查范围内),故 j
- 逻辑分离要清晰:判定(loop)与归类(push)必须分步进行,不可在循环中途基于单次判断就决定归属;
- 进一步性能提示:对更大范围(如 ≤ 10⁶),可将上界优化为 j
通过修正循环结构、明确数学前提、分离关注点,即可写出逻辑严谨、运行稳定的质数筛选程序。










