恰好3个约数计数问题Java代码运行错误排查
问题根因分析
恰好有3个正约数的数有明确的数学特征:只有素数的平方满足要求。对素数
p来说,p²的约数仅为1、p、p²三个,不存在其他约数。因此问题本质是统计所有满足p² ≤ N的素数p的个数,也就是统计[2, sqrt(N)]范围内的素数总数。
你的代码存在两个核心错误:
- 第一个错误(根本原因):素数判断函数
isPrime的循环边界写错
你写的循环终止条件是i * i < N,正确的终止条件应该是i * i <= N。这个bug会导致所有形如(6k±1)²的合数被误判为素数,也刚好完全复现你遇到的所有异常现象:- 当判断
N=25(5²)时,循环初始i=5,i*i=25不满足<25的条件,循环直接不执行,返回true,这就是N=625时25被误判为素数的原因 - 当N≤15624时,
sqrt(N)<125,范围内只有25这一个被误判的合数,所以仅多1个计数,看起来结果“正常” - 当判断
N=121(11²)时,循环执行到i=11时,i*i=121同样不满足<121的条件,不会执行取模判断,直接返回true;N=15625时sqrt(N)=125,范围内除了25,还有121被误判为素数,所以正好多出2个计数 - 随着N继续增大,17²=289、23²=529等更多满足
p=6k±1的素数的平方都会被误判,误差会越来越大。
- 当判断
- 第二个错误:你加的
leakCnt补丁完全是错误的硬编码逻辑
这个补丁只是手动跳过了25、625、15625……等5的偶次幂,完全没有覆盖其他素数平方被误判的场景(比如121、289等),N越大漏判的错误越多,属于没找到根因的临时凑数逻辑,必须全部删除。
修复后的正确代码
class Solution { public int exactly3Divisors(int N) { int count = 0; // 遍历所有满足i² <=N的数,统计其中素数的个数即可 for (int i = 2; i * i <= N; i++) { if (isPrime(i)) { count++; } } return count; } static boolean isPrime(int n) { if (n == 1) return false; if (n == 2 || n == 3) return true; if (n % 2 == 0 || n % 3 == 0) return false; // 修复循环边界:将 < 改为 <= for (int i = 5; i * i <= n; i += 6) { if (n % i == 0 || n % (i + 2) == 0) { return false; } } return true; } }
验证说明
- N=625时,sqrt(N)=25,范围内素数共9个(2、3、5、7、11、13、17、19、23),返回结果9,和预期一致
- N=15625时,sqrt(N)=125,范围内素数共34个,返回结果34,不会出现多计数问题
- 更大规模输入下,只要
i*i不超过int类型取值范围,逻辑都能正常运行,不会出现之前的累计误差。
内容的提问来源于stack exchange,提问作者Chandrakant Dubey
相关产品推荐
相关产品推荐

