记住DP解决方案 - 改变

时间:2014-06-28 08:16:10

标签: python algorithm dynamic-programming

最近我读了练习DP的问题。我无法想出一个,所以我尝试了一个递归解决方案,后来我修改了使用memoization。问题陈述如下: -

  

进行变革。您将获得n种值的硬币面额   v(1)< v(2)< ......< v(n)(所有整数)。假设v(1)= 1,那么你可以   总是为任何金额做出改变C.给出一个算法   使用尽可能少的硬币更改金额C.   [关于问题集4]

我从here

那里得到了问题

我的解决方案如下: -

def memoized_make_change(L, index, cost, d):
    if index == 0:
        return cost

    if (index, cost) in d:
        return d[(index, cost)]

    count = cost / L[index]
    val1 = memoized_make_change(L, index-1, cost%L[index], d) + count
    val2 = memoized_make_change(L, index-1, cost, d)

    x = min(val1, val2)
    d[(index, cost)] = x
    return x

这就是我理解我对问题的解决方案的方式。假设面额以升序存储在L中。当我从头到尾迭代时,我可以选择选择面额或不选择面额。如果我选择它,我会递减以满足较低面额的剩余金额。如果我不选择它,我会递减以满足当前金额较低的面额。

无论哪种方式,在给定的函数调用中,我找到满足给定量的最佳(最低计数)。

我是否可以帮助将思维过程从此处转移到DP解决方案?我没有像任何硬件那样做这个,这只是为了娱乐和练习。我也不需要任何代码,只是帮助解释思维过程是完美的。

[编辑]

我记得读过函数调用很昂贵,这就是为什么自下而上(基于迭代)可能是首选的原因。这可能是这个问题吗?

2 个答案:

答案 0 :(得分:1)

以下是将备忘的递归解决方案转换为传统解决方案的一般方法。在可能的情况下自下而上的DP。

首先,让我们来表达我们的通用" memoized递归解决方案"。这里,x表示每次递归调用时更改的所有参数。我们希望这是一个正整数的元组 - 在你的情况下,(index, cost)。我省略了递归的任何事情(在你的情况下,L),我想我有一个全局cache。 (但是FWIW,在Python中你应该使用标准库lru_cache模块中的functools装饰器,而不是自己管理缓存。)

To solve for(x):
    If x in cache: return cache[x]
    Handle base cases, i.e. where one or more components of x is zero
    Otherwise:
        Make one or more recursive calls
        Combine those results into `result`
        cache[x] = result 
        return result

动态编程的基本思想是先评估基本案例并向上工作:

To solve for(x):
    For y starting at (0, 0, ...) and increasing towards x:
        Do all the stuff from above

然而,当我们以这种方式安排代码时会发生两件巧妙的事情:

  1. 只要正确选择y值的顺序(当然,当只有一个向量组件时这是微不足道的),我们可以安排递归调用的结果总是在缓存中(即我们之前已经计算过它们,因为y在循环的前一次迭代中具有该值)。因此,我们不是实际进行递归调用,而是直接用缓存查找替换它。

  2. 由于y的每个组件都将使用连续增加的值,并且将按顺序放置在缓存中,我们可以使用多维数组(嵌套list s,否则为Numpy array)存储值而不是字典。

  3. 所以我们得到类似的东西:

    To solve for(x):
        cache = multidimensional array sized according to x
        for i in range(first component of x):
            for j in ...:
                (as many loops as needed; better yet use `itertools.product`)
                If this is a base case, write the appropriate value to cache
                Otherwise, compute "recursive" index values to use, look up
                the values, perform the computation and store the result
        return the appropriate ("last") value from cache
    

答案 1 :(得分:0)

我建议考虑你正在构建的值与你需要的值之间的关系。

在这种情况下,您将根据以下内容构建索引值,成本:

index-1 and cost
index-1 and cost%L[index]

您正在寻找的是一种迭代选择的方式,这样您就可以预先计算出所需的一切。

在这种情况下,您只需将代码更改为迭代方法:

for each choice of index 0 upwards:
    for each choice of cost:
        compute value corresponding to index,cost

在实践中,我发现迭代方法对于简单问题可以明显更快(例如* 4),因为它避免了函数调用的开销并检查缓存中是否存在预先存在的值。

相关问题