python - 动态规划 - 使用一组整数计算总和的方法数

标签 python algorithm dynamic-programming

我回答了这个问题

Number of ways to add up to a sum S with N numbers Find all ways to sum given number (with repetitions allowed) from given set

不太明白那里的答案,

我写了两个方法来解决一个问题:

使用数字 N 求和 S 的方法数(允许重复)

例如。 sum = 4 and numbers = 1,2,3 答案是 1111, 22, 1122, 31, 13, 1212, 2112, 2212

在一种方法中我使用内存,而在另一种方法中我不使用。不知何故,在我的机器上,memoize 版本比非 memoized 版本运行得慢

这两种解决方案都有效。

内存版本:

def find_denomination_combinations(amount, denominations):
    memo = {}

    def calculate_combinations(max_amt):
        return_list = list()

        for denomination in denominations:
            new_sum = max_amt - denomination
            if new_sum == 0:
                return_list.append([max_amt])
                return return_list
            elif new_sum < 0:
                return [[]]
            else:
                if new_sum in memo:
                    combi_list = memo[new_sum]
                else:
                    combi_list = calculate_combinations(new_sum)
                for combination in combi_list:
                    if new_sum in memo and combination[:] not in memo[new_sum]:
                        memo[new_sum].append(combination[:])
                    else:
                        memo[new_sum] = []
                        memo[new_sum].append(combination[:])
                    combination.append(denomination)
                    return_list.append(combination)
        return return_list

    result = calculate_combinations(amount)
    return result

非内存版本

def find_denomination_combinations_nmemo(amount, denominations):

    def calculate_combinations(max_amt):
        return_list = list()

        for denomination in denominations:
            new_sum = max_amt - denomination
            if new_sum == 0:
                return_list.append([max_amt])
                return return_list
            elif new_sum < 0:
                return [[]]
            else:
                combi_list = calculate_combinations(new_sum)
                for combination in combi_list:
                    combination.append(denomination)
                    return_list.append(combination)
        return return_list

    result = calculate_combinations(amount)
    return result

我的算法是:

[T(sum-D)] 对于每个 D,其中 D 属于给定的整数集

如果输入总和 = 16 且整数集 = [1,2,3]

非内存版本运行时间为 0.3 秒,内存版本需要 5 秒

最佳答案

我相信 memoized 版本比较慢,因为它使用复杂的代码来更新最外层 else block 中的 memo dict。它可以简单得多:

if new_sum in memo:
    combi_list = memo[new_sum]
else:
    combi_list = memo[new_sum] = calculate_combinations(new_sum)
for combination in combi_list:
    return_list.append(combination + [denomination])

这要快得多。通过此修复,在大多数情况下,内存版本应该比非内存代码更快。

还有其他问题。如果您的 denominations 列表未按升序排序或面额值之间存在间隙,您将得到错误的结果。基本上,任何可能导致 elif 案例被命中的情况都会给出错误的结果。

这是纠正这些问题的 for 循环体的一个版本:

new_sum = max_amt - denomination
if new_sum == 0:
    return_list.append([max_amt]) # don't return here, to allow unsorted denominations!
elif new_sum > 0:
    if new_sum in memo:
        combi_list = memo[new_sum]
    else:
        combi_list = memo[new_sum] = calculate_combinations(new_sum)
    for combination in combi_list:
        return_list.append(combination + [denomination])
# do nothing for new_amt < 0

不过,您可以进一步简化事情,方法是让每个调用都将自己的结果保存在备忘录字典中,而不是依赖调用者这样做,并结合基本情况逻辑(对于 new_sum == 0) 与内存。我还重命名或删除了几个变量:

def find_denomination_combinations_blckknght(amount, denominations):
    memo = {0: [[]]} # prefill value for base case of calculate_combinations where amt==0

    def calculate_combinations(amt):
        if amt not in memo:
            memo[amt] = []
            for denomination in denominations:
                new_amt = amt - denomination
                if new_amt >= 0:
                    for combination in calculate_combinations(new_amt):
                        memo[amt].append(combination + [denomination])
                # do nothing for new_amt < 0
        return memo[amt]

    return calculate_combinations(amount)

这会稍微慢一些,可能是因为额外的函数调用,但是代码简单得多,没有任何地方的 elifelse 情况!

关于python - 动态规划 - 使用一组整数计算总和的方法数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23663824/

相关文章:

python - 如何在 Python 中定义全局函数?

java - 归并排序的基本条件

recursion - Mathematica中的动态编程: how to automatically localize and/or clear memoized function's definitions

algorithm - 图节点可以维护对其父节点的引用列表吗?

algorithm - 合并排序中的递归、快速排序和树的遍历

algorithm - 最大化阵列之间的最小距离

dynamics-crm-2011 - 将 EntityReference 转换为 Entity

python - OpenCV Python 和方向梯度直方图

python - 你能推荐一个好的 minhash 实现吗?

python - Google Stackdriver 未按 Google Kubernetes Engine 的预期显示结构条目日志