当前位置: 首页 > 知识库问答 >
问题:

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

淳于哲
2023-03-14

我问了这个问题

求和的方法数S与N个数求和的所有方法从给定集合中求和给定数(允许重复)

不太明白那里的答案,

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

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

sum=4,number=1,2,3答案是1111、22、1122、31、13、1212、2112、2212

在一种方法中我使用记忆,而在另一种方法中我不使用。不知怎的,在我的机器中,记忆版本的运行速度比非记忆版本慢得多

这两种解决方案都有效。

备忘录版本:

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属于给定的整数集

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

非备忘录版本运行0.3秒,备忘录版本需要5秒

共有1个答案

葛磊
2023-03-14

我相信备忘录化版本较慢,因为它使用复杂的代码来更新最外层ore块中的备忘录判决。它可以简单得多:

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])

这要快得多。有了这个修复,在大多数情况下,备忘录化版本应该比非备忘录化代码更快。

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

以下是用于纠正这些问题的循环体的版本:

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

不过,您可以进一步简化事情,方法是让每个调用在备忘录中保存自己的结果,而不是依赖其调用者这样做,并将基本案例逻辑(fornew_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)

这稍微慢一点,可能是因为额外的函数调用,但代码要简单得多,没有elif或else案例!

 类似资料:
  • 主要内容:动态规划算法的实际应用动态规划算法解决问题的过程和分治算法类似,也是先将问题拆分成多个简单的小问题,通过逐一解决这些小问题找到整个问题的答案。不同之处在于,分治算法拆分出的小问题之间是相互独立的,而动态规划算法拆分出的小问题之间相互关联,例如要想解决问题 A,必须先解决问题 B 和 C。 《贪心算法》一节中,给大家举过一个例子,假设有 1、7、10 这 3 种面值的纸币,每种纸币使用的数量不限,要求用尽可能少的纸币拼凑

  • 这里的单元格1代表死单元格。有什么方法可以通过使用DFS或动态编程E.T.C来降低时间复杂性吗?

  • 我应该对两个分区问题的动态规划实现应用什么修改来解决以下任务: 给你一个正整数数组作为输入,表示为C。程序应该决定是否可以将数组划分为两个相等的子序列。您可以从数组中删除一些元素,但不是全部,以使此类分区可行。 例: 假设输入是4 5 11 17 9。如果我们去掉11和17,两个分区是可能的。我问题是,我应该对我的两个分区实现进行什么调整,以确定两个分区是否可能(可能需要或可能不需要删除某些元素)

  • 我目前正在学习动态编程,我无法解决这个问题。有人能给我一个算法吗?:考虑一个有向图G=(V,E),其中每个边都标有一个字母Sigma的字符,我们指定一个特殊的顶点s作为开始顶点,另一个f作为最后顶点。我们说G接受一个字符串a=a1a2。如果有一条从s到f的n条边的路径,其标号拼写为序列a。设计了一个O((V+E)n)动态规划算法来确定a是否被G接受。

  • 动态规划 建议观看MIT算法导论-动态规划中的课程。

  • 我正在为动态编程编写一些复习材料。我需要提出如何划分子问题,计算出基本情况,并提出递归公式。 给定 n 个正整数 a1,a2,...,an、一个数字 k 和一个目标 W,我们希望选择一个子集 T,其总和恰好是 k 个元素,其总和最接近 W。每个元素只能选择一次。定义一个具有 3 个参数的子问题(即 C[x,y,z] = ...)。 我只处理过几个动态编程示例,从未处理过定义子问题时需要3个参数的示