【问题标题】:Counting change in Haskell计算 Haskell 中的变化
【发布时间】:2015-07-18 04:35:07
【问题描述】:

我遇到了counting change的DP问题的以下解决方案:

count' :: Int -> [Int] -> Int
count' cents coins = aux coins !! cents
  where aux = foldr addCoin (1:repeat 0)
          where addCoin c oldlist = newlist
                  where newlist = (take c oldlist) ++ zipWith (+) newlist (drop c oldlist)

它比我天真的自上而下递归解决方案运行得快得多,我仍在努力理解它。

我知道给定一个硬币列表,aux 计算正整数的每个解。因此,数量的解决方案是在该位置索引列表。

不过,我对addCoin 不太清楚。它以某种方式使用每个硬币的价值从硬币列表中提取元素?我正在努力寻找它的直观含义。

aux 中的折叠也将我的大脑打结了。为什么1:repeat 0是初始值?它代表什么?

【问题讨论】:

    标签: haskell dynamic-programming


    【解决方案1】:

    这是针对问题的命令式 DP 算法的直接翻译,如下所示(在 Python 中):

    def count(cents, coins):
        solutions = [1] + [0]*cents # [1, 0, 0, 0, ... 0]
        for coin in coins:
            for i in range(coin, cents + 1):
                solutions[i] += solutions[i - coin]
        return solutions[cents]
    

    特别是addCoin coin solutions对应

    for i in range(coin, cents + 1):
        solutions[i] += solutions[i - coin]
    

    除了addCoin 返回一个修改后的列表而不是改变旧的列表。对于 Haskell 版本,结果应该在开头有一个不变的部分,直到 coin-th 元素,然后我们必须实现 solutions[i] += solutions[i - coin]

    我们通过take c oldlist实现未更改的部分,通过zipWith (+) newlist (drop c oldlist)实现修改的部分。在修改后的部分,我们将旧列表的i-th 元素和结果列表的i - coin-th 元素相加。索引的移动隐含在 droptake 操作中。

    这种移位和递归定义的一个更简单、经典的例子是斐波那契数:

    fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
    

    我们会强制写成

    def fibs(limit):
        res = [0, 1] + [0]*(limit - 2)
        for i in range(2, limit):
            res[i] = res[i - 2] + res[i - 1]
        return res
    

    回到硬币变化,foldr addCoin (1:repeat 0) 对应于 solutions 的初始化和硬币上的 for 循环,变化是初始列表是无限的而不是有限的(因为懒惰让我们这样做)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多