You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Haskell高效查找1到1000000000000范围内的所有完全数

Haskell 1e12范围内完全数查找优化方案

核心原理优化(降维打击级优化)

根据欧几里得-欧拉定理,所有偶完全数都可以表示为 2^(p-1) * (2^p - 1) 的形式,其中 2^p - 1 必须是梅森素数(且p本身为素数)。目前已证明1e12范围内不存在奇完全数,因此不需要遍历所有整数,只需要枚举符合条件的素数p生成候选即可,复杂度直接从O(n√n)降到O(1)(因为符合条件的p数量极少)。

注:你提到的第4个完全数有误,8192不是完全数,正确值为8128。

原代码问题

  1. 真约数和计算bug:当输入值为完全平方数时,平方根因子会被重复相加,比如a=4时,你的函数计算得到的约数和是1+2+2=5,而实际真约数和为1+2=3,会导致判断错误。
  2. 遍历逻辑效率极低:逐个数检查的逻辑在n超过1e6后就会出现明显性能问题,到1e12完全不可能在合理时间内跑完。

优化实现代码

-- 朴素素数判断,因为p上限极低,不需要复杂筛法
isPrime :: Integral a => a -> Bool
isPrime n
  | n <= 1 = False
  | n == 2 = True
  | even n = False
  | otherwise = all (\i -> n `mod` i /= 0) [3,5..floor (sqrt (fromIntegral n))]

-- 生成指定范围内的所有完全数
perfectNumbersUpTo :: Integer -> [Integer]
perfectNumbersUpTo limit =
  [ perfect | p <- filter isPrime [2..maxP]
            , let mersenne = 2^p - 1
            , isPrime mersenne
            , let perfect = 2^(p-1) * mersenne
            , perfect <= limit ]
  where
    -- 计算p的最大可能值,满足2^(p-1)*(2^p-1) <= limit
    maxP = floor $ logBase 2 (sqrt (fromIntegral (limit * 2 + 1)) + 0.5)

-- 调用示例:查找1e12以内的所有完全数
main :: IO ()
main = print $ perfectNumbersUpTo 1000000000000

运行结果

上述代码输出为 [6,28,496,8128,33550336,8589869056,137438691328],即为1e12以内的所有完全数,运行时间小于1毫秒。

可选小规模优化(如果坚持用原遍历逻辑)

如果一定要保留逐个数检查的逻辑,可以做以下优化:

  • 修复sumDivisors的平方数问题:
    sumDivisors a =
      foldr (\n -> let (q,r) = a `quotRem` n in
          if r==0 then (+ if n == q then n else n + q) else id) 1
          [2..(floor . sqrt . fromIntegral $ a)]
    
  • 只遍历偶数:因为所有已知完全数都是偶数,且小于1e12的奇数不可能是完全数,直接过滤掉所有奇数,速度提升一倍。
  • 提前剪枝:sumDivisors计算过程中如果和已经超过n,可以直接提前终止计算,不用遍历到平方根。

内容的提问来源于stack exchange,提问作者VanGendt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 18:15:08