15

剧透:我正在研究http://www.spoj.pl/problems/KNAPSACK/所以如果你不想为你破坏可能的解决方案,请不要偷看。

样板:

import Data.Sequence (index, fromList)
import Data.MemoCombinators (memo2, integral)

main = interact knapsackStr

knapsackStr :: String -> String
knapsackStr str = show $ knapsack items capacity numItems
  where [capacity, numItems] = map read . words $ head ls
        ls = lines str
        items = map (makeItem . words) $ take numItems $ tail ls

一些设置舞台的类型和助手:

type Item = (Weight, Value)
type Weight = Int
type Value = Int

weight :: Item -> Weight
weight = fst

value :: Item -> Value
value = snd

makeItem :: [String] -> Item
makeItem [w, v] = (read w, read v)

主要功能:

knapsack :: [Item] -> Weight -> Int -> Value
knapsack itemsList = go
  where go = memo2 integral integral knapsack'
        items = fromList $ (0,0):itemsList
        knapsack' 0 _ = 0
        knapsack' _ 0 = 0
        knapsack' w i | wi > w    = exclude
                      | otherwise = max exclude include
          where wi = weight item
                vi = value item
                item = items `index` i
                exclude = go w (i-1)
                include = go (w-wi) (i-1) + vi

这段代码有效;我尝试插入 SPOJ 示例测试用例,它会产生正确的结果。但是当我将这个解决方案提交给 SPOJ(而不是导入 Luke Palmer 的 MemoCombinators,我只是将必要的部分复制并粘贴到提交的源中)时,它超过了时间限制。=/

我不明白为什么;我之前问过一种执行 0-1 背包的有效方法,我相当确信这几乎是最快的:一个记忆函数,它只会递归地计算它绝对需要的子条目以产生正确的结果。我是否以某种方式搞砸了记忆?这段代码中是否有我遗漏的慢点?SPOJ 只是对 Haskell 有偏见吗?

我什至把{-# OPTIONS_GHC -O2 #-}提交的顶部,但唉,它没有帮助。我尝试了一个类似的解决方案,它使用Sequences 的 2D 数组,但它也因为太慢而被拒绝。

4

2 回答 2

4

有一个主要问题确实减慢了这一速度。它太多态了。函数的类型专用版本可以比多态变种快得多,并且无论出于何种原因,GHC 都没有将此代码内联到可以确定使用的确切类型的程度。当我将定义更改integral为:

integral :: Memo Int
integral = wrap id id bits

我得到了大约 5 倍的加速;我认为它的速度足以被 SPOJ 接受。

然而,这仍然比 gorlum0 的解决方案慢得多。我怀疑原因是因为他使用的是数组,而您使用的是自定义的 trie 类型。使用 trie 会占用更多内存,并且由于额外的间接、缓存未命中等原因,查找速度也会变慢。如果您在 IntMap 中严格和取消装箱字段,您可能能够弥补很多差异,但我不确定这是可能的。尝试严格化字段BitTrie会为我创建运行时崩溃。

纯 haskell 记忆代码可能很好,但我认为它不如做不安全的事情快(至少在引擎盖下)。您可以应用Lennart Augustsson 的技术来查看它是否在记忆方面表现更好。

于 2011-10-18T14:02:02.653 回答
0

减慢 Haskell 速度的一件事是 IO,Haskell 中的 String 类型提供了 UTF8 支持,而 SPOJ 不需要它。ByteString 的速度非常快,因此您可能需要考虑使用它们。

于 2016-10-22T17:43:30.593 回答