递归阶乘算法计算46342!时结果异常,求故障原因
问题根源:int类型溢出导致RecursiveCounter计算失效
你碰到的这个问题其实是个典型的整数溢出坑,咱们对着RecursiveCounter的代码就能把原因说透:
核心问题代码
看RecursiveCounter里处理小区间的逻辑:
// 左侧区间处理 if (middle - from > 1) left = count(from, middle); else left = new BigInteger(String.valueOf(from * middle)); // 右侧区间处理 if (to - (middle + 1) > 1) right = count(middle + 1, to); else right = to == middle + 1 ? new BigInteger(String.valueOf(to)) : new BigInteger(String.valueOf((middle + 1) * to));
这里的from * middle和(middle + 1) * to是用int类型直接做乘法的,但Java里int的取值范围是-2^31到2^31-1(也就是-2147483648到2147483647)。咱们算个简单的数:√2147483647 ≈ 46340.95,也就是说只要两个大于等于46341的整数相乘,结果就会超过int的最大值,触发有符号整数溢出——溢出后数值会变成一个错误的负数或乱码值,再转成BigInteger自然就彻底错了。
当你计算46342!时,递归拆分到某个小区间(比如from=46341、middle=46342),from * middle的乘积直接突破int上限,溢出后得到错误值,这就导致整个递归分支的结果出错,最终的阶乘结果自然不对。
为什么其他计数器没问题?
- LoopCounter:每次都是把单个int转成BigInteger再相乘(
result.multiply(new BigInteger(String.valueOf(i)))),从来不会直接做两个int的乘法,完全避开了溢出风险。 - ParallelCounter:当拆分到阈值以下时,调用的是LoopCounter的顺序计算逻辑,同样是单个int转BigInteger相乘,所以也不会触发溢出。
修复方案
把RecursiveCounter里直接做int乘法的地方,改成用BigInteger来计算:
// 左侧修改后 left = BigInteger.valueOf(from).multiply(BigInteger.valueOf(middle)); // 右侧修改后 right = BigInteger.valueOf(middle + 1).multiply(BigInteger.valueOf(to));
这样就彻底避免了int溢出的问题,不管计算多大的阶乘,小区间的乘积都会用BigInteger正确计算。
内容的提问来源于stack exchange,提问作者Leonid Bor
相关产品推荐
相关产品推荐

