【问题标题】:0-1 Knapsack: Find Solution Set in Space-Optimised Implementation0-1 背包:在空间优化实施中寻找解决方案集
【发布时间】:2019-01-27 05:56:12
【问题描述】:

我想解决最大重量约为 200k 且元素超过 100k 的 0-1 背包问题,并最终确定项目集,而不仅仅是最佳重量。

在研究 0-1 背包时,我读到解决此问题的常用方法是通过动态规划并创建一个包含子问题的最佳解决方案的表格,从而将原始问题分成更小的部分,然后在表格上回溯以确定项目集。最大利润,不考虑所用物品,可以以一种内存有效的方式计算(如here 所述)。

这里明显的问题是,对于我想到的维度,这种方法会消耗比可行更多的内存(需要O(n*W) 空间,n 是元素数,W 是最大容量)。进一步研究后,我发现提到(例如,here,另见 Kellerer、Pferschy 和 Pisinger 的“背包问题”)一种解决 0-1 背包的内存有效方法。

我们首先将设置的项目分成两个子集,大小大致相等。在给定原始最大重量W 的情况下,我们将这两个子集视为它们自己的背包问题,并以节省内存的方式确定两个子集的最大利润计算的最后一行(详见上文)。

下一步是找出最佳分割两个子集的位置。为此,我们确定两行的重量w1w2 的最大利润。据我了解,维护w1 + w2 = W 至关重要,因此我遍历第一行并在当前索引的另一端获取索引。我当前对这一步的实现如下所示:

def split(weights, values, n, w, i):
    # s1 is the bigger subset size if n is not even
    s1 = n // 2 + (n&1)
    s2 = n // 2

    row1 = maximum_profit(weights, values, s1, w)
    row2 = maximum_profit(weights[s1:], values[s1:], s2, w)

    max_profits_for_capacity = [x + y for x, y in zip(row1, row2[::-1])]
    max_profits = max(max_profits_for_capacity)                           
    optimal_weight_index = max_profits_for_capacity.index(max_value)

    c1 = row1[optimal_weight_index]
    c2 = row2[w-optimal_weight_index-1]

c1c2 是每个子集的最大利润,然后保持c1 + c2 = W。使用这些值,我们递归到每个子集:

split(weights[:s1], values[:s1], s1, c1, i)      
split(weights[s1:], values[s1:], s2, c2, i+s1)

这就是描述让我迷失的地方。最终,这段代码将递归到n == 1,值为w。给定项目索引i 和最大(本地)容量w,我如何确定是否包含元素?

我可以提供一个小的示例数据集来详细说明我的代码的工作原理以及出错的地方。非常感谢。

【问题讨论】:

    标签: python combinatorics knapsack-problem


    【解决方案1】:

    首先,我猜你说cw 和它们作为容量的关系是错误的,但是从利润列表中得到c1c2

    对于问题,通过 split 函数的返回值,您可以定义您要回答的问题类型。

    当您将拆分直接指向n == 1 点并且您希望将拾取物品的索引放入背包时,您可以在此步骤中简单地返回由[0][1] 组成的值作为输出:

    if n == 1:
      if weights[0] < w:
        return [1]
      return [0]
    
    • [1] 表示将项目选择到结果集中
    • [0] 否则

    然后在 split 函数的其他递归步骤中将它们连接成一个,例如:

    def split(..):
      ..
      # since it is lists concatenation
      return split(weights[:s1], values[:s1], s1, c1, i) + split(weights[s1:], values[s1:], s2, c2, i+s1)
    

    结果您将获得大小为n 的列表(用于拆分的项目数),其中包含零和一。

    总复杂度为:

    • O(nWlogn) 时间,因为我们会拆分到 n == 1 步骤
    • O(W) 用于内存,因为我们在递归时总是只存储结果列表的一部分

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-08
      • 1970-01-01
      • 1970-01-01
      • 2011-12-18
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      • 1970-01-01
      相关资源
      最近更新 更多