埃拉托斯特尼筛法只需筛到√n:用≤√max的素数标记其倍数,未被筛者即为素数;求100内素数时上限为10,循环条件应为i ≤ Math.floor(Math.sqrt(n))。

JavaScript 中实现素数筛法(如埃拉托斯特尼筛法)时,关键不是“用循环”,而是“怎么用循环更高效”——核心在于减少冗余标记、合理设定边界、避免重复计算。
只筛到 √n 就够了
判断一个数是否为素数,只需检查它是否有 ≤ √n 的因子;同理,在筛法中,只需把 ≤ √max 的素数的倍数筛掉即可。超过 √max 的数,若未被筛过,本身就是素数。
- 比如求 100 以内素数,只需用 2 到 10(即 ⌊√100⌋)之间的素数去筛
- 循环上限写成 Math.floor(Math.sqrt(n)),别用 Math.sqrt(n) + 1 或直接 i * i (后者更简洁且避免浮点误差)
从 p² 开始标记倍数
每个素数 p 的倍数中,2p、3p…(p−1)p 都已被更小的素数筛过。真正需要标记的是 p²、p²+p、p²+2p…
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 例如 p = 5,不用筛 10、15、20(它们已被 2 或 3 筛过),直接从 25 开始
- 内层循环写成 for (let j = p * p; j
- 注意:p * p 可能溢出(对极大 n),但一般前端场景(n ≤ 1e6)无需担心
跳过偶数,空间时间双优化
除 2 外,所有偶数都不是素数。可单独保留 2,然后只处理奇数,把数组索引映射为 2k+1(即第 k 个奇数)。
立即学习“Java免费学习笔记(深入)”;
- 创建长度为 Math.floor((n - 1) / 2) 的布尔数组,isOddPrime[k] 表示 2k+3 是否为素数
- 外层循环从 3 开始,步长为 2:for (let p = 3; p * p
- 内层标记也按奇数倍数推进,起始点仍为 p²(是奇数),步长为 2p(保证每次加的是偶数,落在奇数位置上)
用 Uint8Array 替代普通数组(可选进阶)
布尔数组底层仍是 32 位整数,内存浪费大。用 Uint8Array 每个元素只占 1 字节,且初始化快、缓存友好。
- const sieve = new Uint8Array(n + 1); // 0 和 1 默认为 0(false)
- 标记时用 sieve[j] = 1;,最后收集时 if (!sieve[i]) result.push(i);
- 对百万级 n,内存可减少约 75%,速度提升 10%–20%

















