我编译了这个程序,并且正在尝试运行它。
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很新。)我该如何修复它?我是否必须将collatzLength重写为尾递归?
答案 0 :(得分:11)
作为相关代码的作者,我现在有点尴尬,因为它只有两个可能的堆栈溢出错误。
它使用Int
。在32位系统上,这将溢出,因为Collatz序列可能比起始编号高很多。当函数在负值和正值之间来回跳转时,这种溢出会导致无限递归。
在数字介于1和100之间的情况下,最差的起点是704511,高达56,991,483,520,然后再回落到1.这远远超出了32位范围。
它使用maximumBy
。此函数不严格,因此在长列表上使用时会导致堆栈溢出。使用默认堆栈大小,一百万个元素就足以实现这一点。但是,由于GHC进行了严格的分析,它仍然可以启用优化。
解决方案是使用严格的版本。由于标准库中没有可用的,我们可以自己使用严格的左侧折叠。
这是一个更新版本,即使没有优化,也应该(希望)堆栈无溢出。
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
使其无法使用-O2
。 Prelude
的{{1}}的实现是懒惰的,因此对于没有编译器魔法灰尘的长列表来说也不太适用。
答案 2 :(得分:6)
我认为你最有可能用一些Collatz序列击中整数溢出,然后最终进入一个包含溢出但从未命中1的“人工”循环。这将产生无限递归。
请记住,一些Collatz序列在它们最终(?)结束之前会比它们的起始数字大得多。
尝试查看是否修复了您使用Integer
代替Int
的问题。
答案 3 :(得分:4)
每当您担心性能时,请使用优化程序(通过-O2
标志)。 GHC的优化不仅对运行时间而且对堆栈使用非常重要。我用GHC 7.2对此进行了测试,优化可以解决您的问题。
编辑:此外,如果您使用的是32位计算机,请务必使用Int64
或Word64
。你将溢出32位int的大小,否则会导致非终止(感谢Henning为此,请回答他的答案)。