【问题标题】:Is this memoization working properly?这种记忆是否正常工作?
【发布时间】:2023-04-06 11:36:02
【问题描述】:

我在 Haskell 中一直致力于解决 Project Euler #14 一段时间,但由于某种原因,我无法使其正常工作。不久前我使用 Groovy 解决了这个问题,我想我在这里使用的方法基本相同。然而,即使只是找到前 10,000 个长度,程序运行速度也非常慢,我现在真的不知道为什么。我认为我正确使用了 memoization,但是即使 GHCI 中的数据集很小,我的内存也会用完。

这是我到目前为止的想法。

collatz = (map collatz' [0..] !!)
    where collatz' n
        | n == 1 = 1
        | n `mod` 2 == 0 = 1 + collatz (n `div` 2)
        | otherwise = 1 +  collatz (3 * n + 1)

我会运行 map collatz [1..1000000] 来获得问题的答案,但 map collatz [1..10000] 给我一个内存不足错误,并且还需要几秒钟才能完成运行。

如果有人能给我一些关于这个程序的问题的见解,那就太好了!我已经尝试了很多东西,但我只是卡住了,需要帮助。

谢谢!

【问题讨论】:

  • 你试过编译运行它吗?
  • 我刚做了。我没有遇到1..10000 的内存不足错误,但它仍然花费了相同的时间。我确实遇到了数据集1..100000 的内存不足错误,而且速度也很慢。
  • 使用列表进行记忆不是解决这个问题的好选择。涉及很多索引,每个都需要 O(n) 时间。
  • 您可以尝试现有的记忆库之一,例如MemoTrie。许多想法可以在 Haskell wiki 的 Memoization 页面找到。
  • 另外data-memocombinators使用也很简单,你的collatz变成collatz = integral collatz' where {- ... -}。我几乎立即得到map collatz [1..10000] 的结果。

标签: haskell memoization collatz


【解决方案1】:

记忆在这里工作得很好。事实上,它运行得如此之好,以至于它填满了你所有的记忆。 Collat​​z 序列的中间项变得相当大。从11000000 的任何序列中出现的最大项是数字2974984576。所以这是您尝试在内存中构建的列表的长度。

另一方面,直接实现 Collat​​z 函数而不进行记忆应该可以很好地解决这个问题。

【讨论】:

  • 将数字记忆到一定限度也可能是可行的方法。
  • 这实际上很有意义。谢谢!我猜在 Groovy 中我使用的是 Map 而不是列表。我正在使用的实现在不使用 memoization 的情况下仍然很慢,所以我必须想出另一种方法来加快它,哈哈。
  • 我用ghc -O2 做了一些测试,结果证明没有记忆的版本确实是最快的。记忆前 200000 - 6.09 秒;完全没有记忆 - 5.80 秒;同样使用带有严格累加器的工人包装器会导致运行时间为 5.41 秒(请注意,我在所有测试中都使用了 rem 而不是 mod)。使用maximum . map collatz $ [1..1000000] 测试。
  • @Vitus 你用的是Integer 我猜?如果您根据类型使用IntWord(根据需要使用64 位GHC,使用32 位,您需要多次进入Integer),您可以将其加速大约 10 倍。一个好的记忆然后给出另一个大约 6 倍。
  • @DanielFischer:是的,因为没有适用于 Windows 的 64 位 GHC(至少据我所知),我只是在上面打了 Integer 并没有打扰任何更复杂的东西。看来我应该考虑到这一点,谢谢!
猜你喜欢
  • 2016-09-18
  • 1970-01-01
  • 2019-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-17
  • 2011-11-08
相关资源
最近更新 更多