通过记忆化以最小面额进行更改

时间:2018-03-25 08:09:17

标签: haskell dynamic-programming memoization

我想知道如何使用memoization制作有效的算法。特别是,有没有办法在Haskell中为索引值设置O(1)访问时间?

这是the problem described in detail。这是我对递归算法的尝试:

denom :: (Int, Int) -> [Int] -> Int
denom (_, 0) _ = 0
denom (0, _) _ = (maxBound :: Int) - 1000 -- subtracting 1000 otherwise overflows
denom (i, j) di
  | v > j = denom (i-1, j) di
  | otherwise = min (denom (i-1, j) di) (1 + denom (i, j-v) di)
  where v = di !! (i - 1)

另外,我如何在Haskell中声明INFINITY以便min在所有情况下都有效?

1 个答案:

答案 0 :(得分:1)

首先,要在Haskell中进行O(1)访问,标准的首选库是Data.Array

第二,定义某种类型附近的东西的一般方法,但不知何故"外部"它是使用Maybe类型;这是我为INFINITY推荐的内容。另外,我认为这在算法中更有意义,因为INFINITY实际上意味着"我们无法用这组面额来表达这个值#34;而不是"我们可以用无限数量的硬币制作这个值"。

因此,使用Maybe,我们要定义的第一件事是min的版本Maybe Int

myMin :: Ord a => Maybe a -> Maybe a -> Maybe a
myMin (Just a) (Just b) = Just $ min a b
myMin Nothing x = x
myMin x Nothing = x

然后,使用我们可以使用链接页面中给出的算法来解决此问题:

minCoinCoint :: Int -> [Int] -> Maybe Int
minCoinCoint target denoms = res (target, length denoms)
  where
    denomArray = listArray (0, length denoms) (0:denoms)
    myArrayBounds = ((0, 0), (target, length denoms))
    myArray = array myArrayBounds [(i, res i) | i <- range myArrayBounds]
    res (_, 0) = Nothing
    res (0, _) = Just 0
    res (t, d) = let dval = denomArray ! d
                     prev1 = myArray ! (t, d-1)
                     prev2 = if t >= dval
                             then (+1) <$> (myArray ! (t-dval, d))
                             else Nothing
                 in myMin prev1 prev2

就是这样。 (好吧,假设你还记得文件顶部的import Data.Array行)

请注意,myArray是通过引用res构建的,res通过查找myArray中的值来进行所有递归调用。

这种语法可能有点令人困惑:

(+1) <$> (myArray ! (t-dval, d))

这样做是因为记住myArray的每个元素都不是Int而是Maybe Int。该语法说&#34;将函数(+1)应用于值(myArray ! (t-dval, d))&#34;的内部,以便Just 4成为Just 5,但是Nothing将保持为Nothing