对于SPOJ,这个memoized DP表的速度有多慢?

时间:2011-09-22 03:29:08

标签: performance haskell memoization knapsack-problem

SPOILERS:我正在研究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,我只是将必要的部分复制并粘贴到提交的源中)时,它超出了时间限制。 = /

我不明白为什么;我asked earlier关于执行0-1背包的有效方法,我相信它的速度和它一样快:一个memoized函数,只能递归地计算它绝对需要的子条目为了产生正确的结果。我是否以某种方式弄乱了备忘录?我错过了这段代码的慢点吗? SPOJ是否只是偏向于Haskell?

我甚至将{-# OPTIONS_GHC -O2 #-}放在提交的顶部,但唉,它没有帮助。我尝试过使用Sequence s的2D数组的类似解决方案,但它也被拒绝为太慢了。

2 个答案:

答案 0 :(得分:4)

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

integral :: Memo Int
integral = wrap id id bits

我获得了大约5倍的加速;我认为它足够快,可以在SPOJ上被接受。

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

纯粹的haskell记忆代码可能很好,但我不认为它与做不安全的事情一样快(至少在引擎盖下)。您可以申请Lennart Augustsson's technique以查看它在备忘录方面的表现是否更好。

答案 1 :(得分:0)

减缓Haskell的一件事是IO,Haskell中的String类型提供了我们对SPOJ不需要的UTF8支持。 ByteStrings非常快,所以你可能想考虑使用它们。