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

如何避免递归调用耗尽RAM?欧拉计划193的Haskell内存优化

欧拉计划193问题的Haskell内存优化问题

问题背景

我正在解决欧拉计划193问题,目标是计算2^50以下的无平方因子数的数量。
采用的算法基于容斥原理:从所有数中减去包含单个质数平方因子的数,加上包含两个质数平方因子的数,减去包含三个的,以此类推,直到最多8个因子的组合。
为减少组合生成的工作量,我维护一个[(product, index)]列表,例如对于49×121×361的乘积,会保存(2140369,7),表示从索引8开始迭代,用2140369相乘得到更多组合(比如4个数的组合)。
这个算法在Python中可正常运行,耗时20秒,但我的Haskell实现会耗尽RAM,无法完成运行。
我推测问题出在列表base和base'一直占用内存——在Python中,我会在迭代开始时初始化base' = [],迭代结束时执行base = base'.copy(),内存中始终只保留这两个列表,但Haskell里没做到这一点。

核心疑问

  • 如何减少Haskell实现的内存占用?
  • 能否在递归调用之间销毁不再需要的列表?
  • 能否使用流处理(比如惰性求值)来优化?
  • 我认为accum函数是需要修改的核心部分

我的Haskell代码

import qualified Data.ByteString.Lazy.Char8 as BLC
import qualified Data.Vector.Unboxed as U
import Data.Vector.Unboxed ((!))
import Data.Maybe (fromJust)
import Data.List (foldl')

primesSq :: Int -> [BLC.ByteString] -> [Int]
primesSq lim [] = []
primesSq pmax (b:bs) | p < pmax  = p^2 : primesSq pmax bs
                     | otherwise = primesSq pmax []
    where
        (p, _) = fromJust $ BLC.readInt b

solve :: [BLC.ByteString] -> Int -> Int
solve ls limit = total
    where
        pmax = floor . sqrt . fromIntegral $ limit
        ps2 = U.fromList $ primesSq pmax ls
        accumProd = U.takeWhile (< limit) $ U.scanl1 (*) ps2
        rmax = U.length accumProd
        base1 = takeWhile (\b -> fst b <= limit `div` ps2!0) 
                       (zip (U.toList ps2) [0..])
        
        accum :: [(Int,Int)] -> Int -> [Int] -> Int
        accum _ acc [] = acc
        accum base acc (r:rs) = accum base' acc' rs
            where
                base' = [(prod, j) | (p2, i) <- base
                        , j <- [i+1..U.length ps2-1]
                        , let prod = p2 * ps2 ! j 
                        , prod <= limit]
                acc' = acc + (-1)^r * sum [limit `div` p | (p,_) <- base']
        
        total = limit 
                - U.sum (U.map (div limit) ps2) 
                + accum base1 0 [2..rmax]

main :: IO ()
main = do
    ls <- BLC.lines <$> BLC.readFile "primes.txt"
    print $ solve ls (2^50)

注:我没有生成质数,而是从文本文件读取,同时练习文件读取操作。我也曾尝试用scanl'改写accum的递归,但结果相同且代码更冗长。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:53:18