为什么记忆功能在Haskell中会占用大量内存?

时间:2019-05-14 13:46:56

标签: haskell

我写了一段Haskell代码来计算Collatz chain的长度。给定数字n,如果n为偶数,则序列中的下一个数字为n / 2;如果n为奇数,则为3 * n + 1。该序列在收敛到1时结束。我想从某个输入数字以下的任何数字开始查找最长链的长度。

我试图用一个记忆函数来实现长度计算,因为我希望从某些数字开始需要链的长度。因此,从726开始的链的长度将仅仅是1 +从363开始的链的长度,这已经被计算了。我的代码如下所示。

collatz :: Int -> Int
collatz n
    | even n = n `div` 2
    | otherwise = 3 * n + 1

collatzLength :: Int -> Int
collatzLength = (fmap len [0 ..] !!)
    where len 0 = 0
          len 1 = 1
          len n = 1 + (collatzLength . collatz $ n)

maxLengthBelow :: Int -> Int
maxLengthBelow = foldl1 max . fmap collatzLength . enumFromTo 1

main :: IO()
main = print $ maxLengthBelow 10000

此代码有效,但占用大量内存。对它进行概要分析时,按预期输入{10000}运行mainlen仅被调用21664次,但是该程序需要16秒和4.5Gb的内存!什么占用了所有内存?我希望记忆功能可以产生一个快速的,低内存的解决方案。

1 个答案:

答案 0 :(得分:9)

使Collat​​z序列如此有趣的原因之一是,有一些小的起始种子会带您进入1的大气层。特别是9663使其一直延伸到27114424在它崩溃之前-这是一个很长的备忘列表!

而且,就其价值而言,我希望您的备忘录列表每个元素使用三个机器单词:一个用于I#上的Int构造函数,一个用于包含的数字,另一个用于(:)构造函数。让我们问一下存储27114424元素需要多少空间,然后:

> 27114424 * (64*3) / 1024 {-Kb-} / 1024 {-Mb-} / 1024 {-Gb-}
4.8484368324279785

所以4.5Gb听起来不错,甚至可能有点低。