I've written an answer to the bounded knapsack problem with one of each item in Scala, and tried transposing it to Haskell with the following result:
knapsack :: [ ( Int, Int ) ] -> [ ( Int, Int ) ] -> Int -> [ ( Int, Int ) ]
knapsack xs [] _ = xs
knapsack xs ys max =
foldr (maxOf) [ ] [ knapsack ( y : xs ) ( filter (y /=) ys ) max | y <- ys
, weightOf( y : xs ) <= max ]
maxOf :: [ ( Int, Int ) ] -> [ ( Int, Int ) ] -> [ ( Int, Int ) ]
maxOf a b = if valueOf a > valueOf b then a else b
valueOf :: [ ( Int, Int ) ] -> Int
valueOf [ ] = 0
valueOf ( x : xs ) = fst x + valueOf xs
weightOf :: [ ( Int, Int ) ] -> Int
weightOf [ ] = 0
weightOf ( x : xs ) = snd x + weightOf xs
I'm not looking for tips on how to clean up the code, just to get it working. To my knowledge it should be doing the following:
- For each tuple option (in ys)
- if the weight of the current tuple (y) and the running total (xs) combined is less than the capacity
- get the optimal knapsack that contains the current tuple and the current total (xs), using the available tuples (in ys) less the current tuple
- Finally, get the most valuable of these results and return it
*Edit: * Sorry, forgot to say what's wrong... So it compiles alright, but it gives the wrong answer. For the following inputs, what I expect and what it produces:
knapsack [] [(1,1),(2,2)] 5
Expect: [(1,1),(2,2)]
Produces: [(1,1),(2,2)]
knapsack [] [(1,1),(2,2),(3,3)] 5
Expect: [(2,2),(3,3)]
Produces: []
knapsack [] [(2,1),(3,2),(4,3),(6,4)] 5
Expect: [(2,1),(6,4)]
Produces: []
So I was wondering what could be the cause of the discrepancy?
The solution, thanks to sepp2k:
ks = knapsack []
knapsack :: [ ( Int, Int ) ] -> [ ( Int, Int ) ] -> Int -> [ ( Int, Int ) ]
knapsack xs [] _ = xs
knapsack xs ys max =
foldr (maxOf) [ ] ( xs : [ knapsack ( y : xs ) ( ys #- y ) max
| y <- ys, weightOf( y : xs ) <= max ] )
(#-) :: [ ( Int, Int ) ] -> ( Int, Int ) -> [ ( Int, Int ) ]
[ ] #- _ = [ ]
( x : xs ) #- y = if x == y then xs else x : ( xs #- y )
maxOf :: [ ( Int, Int ) ] -> [ ( Int, Int ) ] -> [ ( Int, Int ) ]
maxOf a b = if valueOf a > valueOf b then a else b
valueOf :: [ ( Int, Int ) ] -> Int
valueOf [ ] = 0
valueOf ( x : xs ) = fst x + valueOf xs
weightOf :: [ ( Int, Int ) ] -> Int
weightOf [ ] = 0
weightOf ( x : xs ) = snd x + weightOf xs
Which returns the expected results, above.