Haskell空间溢出

时间:2011-08-21 19:57:55

标签: haskell ghc

我编译了这个程序,并且正在尝试运行它。

import Data.List
import Data.Ord
import qualified Data.MemoCombinators as Memo

collatzLength :: Int -> Int
collatzLength = Memo.arrayRange (1, 1000000) collatzLength'
  where
    collatzLength' 1 = 1
    collatzLength' n | odd n  = 1 + collatzLength (3 * n + 1)
                     | even n = 1 + collatzLength (n `quot` 2)

main = print $ maximumBy (comparing fst) $ [(collatzLength n, n) | n <- [1..1000000]]

我从GHC获得以下内容

Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.

我认为这是我听过的“空间溢出”之一。 (我对Haskell很新。)我该如何修复它?我是否必须将collat​​zLength重写为尾递归?

4 个答案:

答案 0 :(得分:11)

作为相关代码的作者,我现在有点尴尬,因为它只有两个可能的堆栈溢出错误。

  1. 它使用Int。在32位系统上,这将溢出,因为Collat​​z序列可能比起始编号高很多。当函数在负值和正值之间来回跳转时,这种溢出会导致无限递归。

    在数字介于1和100之间的情况下,最差的起点是704511,高达56,991,483,520,然后再回落到1.这远远超出了32位范围。

  2. 它使用maximumBy。此函数不严格,因此在长列表上使用时会导致堆栈溢出。使用默认堆栈大小,一百万个元素就足以实现这一点。但是,由于GHC进行了严格的分析,它仍然可以启用优化。

    解决方案是使用严格的版本。由于标准库中没有可用的,我们可以自己使用严格的左侧折叠。

  3. 这是一个更新版本,即使没有优化,也应该(希望)堆栈无溢出。

    import Data.List
    import Data.Ord
    import qualified Data.MemoCombinators as Memo
    
    collatzLength :: Integer -> Integer
    collatzLength = Memo.arrayRange (1,1000000) collatzLength'
      where
        collatzLength' 1 = 1
        collatzLength' n | odd n  = 1 + collatzLength (3 * n + 1)
                         | even n = 1 + collatzLength (n `quot` 2)
    
    main = print $ foldl1' max $ [(collatzLength n, n) | n <- [1..1000000]]
    

答案 1 :(得分:7)

这是一个较短的程序,以同样的方式失败:

main = print (maximum [0..1000000])

是的。

$ ghc --make harmless.hs && ./harmless
[1 of 1] Compiling Main             ( harmless.hs, harmless.o )
Linking harmless ...
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.

使用-O2可行。我该怎么做?我不知道:(这些太空神秘是一个严重的问题。

修改

向Hammar指出罪魁祸首。

更改您的程序

maximum' = foldl1' max

使其无法使用-O2Prelude的{​​{1}}的实现是懒惰的,因此对于没有编译器魔法灰尘的长列表来说也不太适用。

答案 2 :(得分:6)

我认为你最有可能用一些Collat​​z序列击中整数溢出,然后最终进入一个包含溢出但从未命中1的“人工”循环。这将产生无限递归。

请记住,一些Collat​​z序列在它们最终(?)结束之前会比它们的起始数字大得多。

尝试查看是否修复了您使用Integer代替Int的问题。

答案 3 :(得分:4)

每当您担心性能时,请使用优化程序(通过-O2标志)。 GHC的优化不仅对运行时间而且对堆栈使用非常重要。我用GHC 7.2对此进行了测试,优化可以解决您的问题。

编辑:此外,如果您使用的是32位计算机,请务必使用Int64Word64。你将溢出32位int的大小,否则会导致非终止(感谢Henning为此,请回答他的答案)。