Haskell foldl 'does not save the space that was expected

Trying to implement a simple dynamic programming algorithm for a Knapsack problem. Obviously, this approach uses a lot of memory, so I'm trying to optimize the memory used. I'm just trying to save only the previous row of the table in memory long enough to calculate the next row and so on. At first I thought that my implementation was solid, but it still ran out of memory as an implementation designed to store the entire table. So I thought that I might need it foldl'instead foldr, but it does not make any difference. My program continues to consume memory until my system runs out.

So, I have 2 specific questions:

  • What is my code that uses all the memory? I thought I was smart using the fold because I assumed that only the current battery value would be stored in memory.
  • What is the right approach to achieve my goal; that is, keeping only the very last line in memory? I don’t need the code, perhaps only some useful functions and data types. More generally, what are some tips and techniques for understanding memory usage in Haskell?

Here is my implementation

data KSItem a = KSItem { ksItem :: a, ksValue :: Int, ksWeight :: Int} deriving (Eq, Show, Ord)
dynapack5 size items = finalR ! size
  where
    noItems = length items
    itemsArr = listArray(1,noItems) items
    row = listArray(1,size) (replicate size (0,[]))
    computeRow row item =
      let w = ksWeight item
          v = ksValue item
          idx = ksItem item
          pivot = let (lastVal, selections) = row ! w
                  in if v > lastVal 
                     then (v, [idx])
                     else (lastVal, selections)
          figure r c =
              if (prevVal + v) > lastVal
              then (prevVal + v, prevItems ++ [idx])
              else (lastVal, lastItems)
              where (lastVal, lastItems) = (r ! c)
                    (prevVal, prevItems) = (r ! (c - w))
          theRest = [ (figure row cw) | cw <- [(w+1)..size] ]
          newRow = (map (row!) [1..(w-1)]) ++ 
                   [pivot] ++
                   theRest
      in listArray (1,size) newRow
    finalR = foldl' computeRow row items

In my head, what I think it does is initialize the first line (0, []) ... it repeats as necessary, and then discards the fold, where the next line is calculated based on the supplied line, and this value becomes the battery . I do not see where more and more memory is being consumed ...

: , \\ ?

+4
2

, force . , force . :

let res = listArray (1,size) newRow in force (map fst $ elems res) `seq` res

, .

, . Integers , . , Integer -s :

import qualified Data.Vector as V -- using this instead of Array cause I like it more
import Data.List
import Control.Arrow
import Data.Bits
import Control.DeepSeq

data KSItem a = KSItem { ksItem :: a, ksValue :: Int, ksWeight :: Int} deriving (Eq, Show, Ord)

dynapack5' :: Int -> [KSItem a] -> (Int, Integer)
dynapack5' size items = V.last solutions where
    items' = [KSItem i v w | (i, KSItem _ v w) <- zip [0..] items]

    solutions = foldl' add (V.replicate (size + 1) (0, 0::Integer)) items'

    add arr (KSItem item currVal w) = force $ V.imap go arr where
        go i (v, is) | w < i && v' > v = (v', is')
                     | otherwise       = (v, is)
            where (v', is') = (+currVal) *** (`setBit` item) $ arr V.! (i - w)
+2

Data.Array , , foldl' WHNF , . Control.DeepSeq

in listArray (1,size) newRow

in force (listArray (1,size) newRow)

, , .

, , , .

+1

Source: https://habr.com/ru/post/1531084/


All Articles