Last active
January 4, 2016 22:09
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# 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