Efficient table for Dynamic Programming in Haskell

后端 未结 5 1668
青春惊慌失措
青春惊慌失措 2021-01-31 06:03

I\'ve coded up the 0-1 Knapsack problem in Haskell. I\'m fairly proud about the laziness and level of generality achieved so far.

I start by providing functions for crea

5条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-31 06:30

    First, your criterion for an unboxed data structure is probably a bit mislead. Unboxed values must be strict, and they have nothing to do with immutability. The solution I'm going to propose is immutable, lazy, and boxed. Also, I'm not sure in what way you are wanting construction and querying to be O(1). The structure I'm proposing is lazily constructed, but because it's potentially unbounded, its full construction would take infinite time. Querying the structure will take O(k) time for any particular key of size k, but of course the value you're looking up may take further time to compute.

    The data structure is a lazy trie. I'm using Conal Elliott's MemoTrie library in my code. For genericity, it takes functions instead of lists for the weights and values.

    knapsack :: (Enum a, Num w, Num v, Num a, Ord w, Ord v, HasTrie a, HasTrie w) =>
                (a -> w) -> (a -> v) -> a -> w -> v
    knapsack weight value = knapsackMem
      where knapsackMem = memo2 knapsack'
            knapsack' 0 w = 0
            knapsack' i 0 = 0
            knapsack' i w
              | weight i > w = knapsackMem (pred i) w
              | otherwise = max (knapsackMem (pred i) w)
                            (knapsackMem (pred i) (w - weight i)) + value i
    

    Basically, it's implemented as a trie with a lazy spine and lazy values. It's bounded only by the key type. Because the entire thing is lazy, its construction before forcing it with queries is O(1). Each query forces a single path down the trie and its value, so it's O(1) for a bounded key size O(log n). As I already said, it's immutable, but not unboxed.

    It will share all work in the recursive calls. It doesn't actually allow you to print the trie directly, but something like this should not do any redundant work:

    mapM_ (print . uncurry (knapsack ws vs)) $ range ((0,0), (i,w))
    

提交回复
热议问题