Skip to content

Instantly share code, notes, and snippets.

@AndrasKovacs
Last active January 4, 2016 22:09
Show Gist options
  • Save AndrasKovacs/8686102 to your computer and use it in GitHub Desktop.
Save AndrasKovacs/8686102 to your computer and use it in GitHub Desktop.
Adaptively using the DP knapsack solution or the solution of its dual depending on the weight limit. It's more of an exercise in theory since both functions leak space rather severely.
{-# LANGUAGE LambdaCase #-}
import Data.List
import Control.Arrow
import Control.Lens
inv = [("map",9,150), ("compass",13,35), ("water",153,200), ("sandwich",50,160),
("glucose",15,60), ("tin",68,45), ("banana",27,60), ("apple",39,40),
("cheese",23,30), ("beer",52,10), ("cream",11,70), ("camera",32,30),
("tshirt",24,15), ("trousers",48,10), ("umbrella",73,40),
("waterproof trousers",42,70), ("overclothes",43,75), ("notecase",22,80),
("sunglasses",7,20), ("towel",18,12), ("socks",4,50), ("book",30,10)]
-- produces lists of included items
knapsack01 :: [(String, Int, Int)] -> [(Int, [String])]
knapsack01 = foldr add (repeat (0, [])) where
add (name, w, v) list = cantFit ++ zipWith max skip include where
(cantFit, skip) = splitAt w list
include = list <&> (+v) *** (name:)
-- set of items with minimal value such that (total weight >= limit)
-- in other words, set of items removed from the starting collection
-- returns Nothing when there aren't enough items to satisfy the limit
knapsack01_dual :: [(String, Int, Int)] -> [Maybe (Int, [String])]
knapsack01_dual = foldr remove (Just (0, []): repeat Nothing) where
remove (name, w, v) list = low' ++ zipWith procHigh include skip where
(low, skip) = splitAt w list
low' = low <&> \case a@(Just (v', xs)) | v' < v -> a; _ -> Just (v, [name])
include = list <&> (<&> (+v) *** (name:))
procHigh a b = minimumOf (each . each) [a, b]
-- when the size limit is closer to the total size of the starting collection,
-- we should remove items from it, otherwise we should start with an empty bin and add items.
knapsack01_adaptive :: [(String, Int, Int)] -> Int -> (Int, [String])
knapsack01_adaptive items w = let
maxw = sumOf (each._2) items
maxscore = sumOf (each._3) items
Just (dscore, removed) = knapsack01_dual items !! (maxw - w)
fromPlain = knapsack01 items !! w
fromDual = (maxscore - dscore, map (^._1) items \\ removed) -- this is ugly O(n^2), but I care not here.
in if w < div maxw 2
then fromPlain
else if w < maxw
then fromDual
else (maxscore, map (^._1) items)
main = do -- should give same answer
print $ knapsack01_adaptive inv 500
print $ knapsack01 inv !! 500
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment