【问题标题】:Determine set of items upto max n, which give the smallest total value and whose combined weights >= the capacity weight (knapsack problem variant)确定最大 n 的项目集,它们给出最小的总值并且其组合权重 >= 容量权重(背包问题变体)
【发布时间】:2020-07-27 18:53:31
【问题描述】:

我正在尝试运行背包问题的变体,我需要总价值最小但组合重量等于或超过容量重量的项目组合。

maxn = 3
vm = [60, 100, 120, 50, 10, 10]  # Values
wt = [10, 20, 30, 20, 5, 5]  # Weights
W = 50  # Capacity weight
n = len(vm)

我所拥有的,给了我经典的背包答案(总价值最高):

def knapsack(n, W, wt, vm):
    for i in range(n+1):
        for w in range(W+1):
            if i == 0 or w == 0: 
                K[i][w] = 0
            elif wt[i-1] <= w: 
                K[i][w] = max(vm[i-1]  
                    + K[i-1][w-wt[i-1]],  K[i-1][w]) 
            else: 
                K[i][w] = K[i-1][w]
    print(K[n][W])
    return K[n][W]

def items_in_optimal(n, W, wm):
    i = n
    j = W

    while (i > 0 and j > 0):
        if(K[i][j] != K[i-1][j]):
            print(i-1)
            j = j-wm[i-1]
            i = i-1
        else:
            i = i-1

K = [[0 for i in range(W + 1)] for j in range(n + 1)] 
knapsack(n, W, wt, vm)
items_in_optimal(n, W, wt)

Output: 
220
2
1

我要找的结果是:

Output:
170
3
2

非常感谢任何帮助!

编辑问题更清楚

编辑 2: 这是我想出的,但如果有更快的方法,我会非常感兴趣:

from itertools import combinations
import numpy as np

rlen = [2, maxn]
a = []
for r in rlen:
    best_value = sum(vm)
    for i in combinations(np.arange(0, len(vm)), r):
        if sum(np.array(wt)[list(i)]) >= W:
            if sum(np.array(vm)[list(i)]) < best_value:
                best_value = sum(np.array(vm)[list(i)])
                best_indices = list(i)
    a.append([r, best_value, best_indices])

split_inv = min(a, key=lambda t: t[1])[2]
print(split_inv)

【问题讨论】:

  • 解决经典变体并输出未包含在解决方案中的项目。弄清楚容量应该是多少。

标签: python algorithm dynamic-programming knapsack-problem


【解决方案1】:

创建一个二维表,其中行表示从 0 到 W 的权重,列表示从 0 到 N 的项数。每个条目应该是一个元组:(valid, value, indexList)

最初,只有table[0][0] 有效。

对于每个项目,反向扫描表格。换句话说,循环看起来像这样:

items = zip(wt, vm)
for itemIndex, (itemWeight, itemValue) in enumerate(items):
    for w in range(W,-1,-1):
        for n in range(N-1,-1,-1):

请注意,n 循环从 N-1 开始。表的 N 列中的条目表示 N 项的列表,因此不能将其他项添加到该条目中。

当您找到有效的条目table[w][n] 时,通过将当前项目添加到该条目来计算新的权重、值和索引列表。 (如果w + itemWeight大于W,那么新的权重就是W。)然后与候选条目table[newWeight][n+1]进行比较。如果候选者无效或具有更高的值,则更新候选者。如果候选项有效且值较低,则不要更改。

扫描完所有项目后,在表的最后一行中找到具有最低值的条目,这就是答案。

对于给定的示例,最终表格如下所示。仅显示具有有效条目的行。第一列是该行的权重。

 0 [(True , 0, []), (False,  0, []),  (False,  0, []),     (False,  0, [])]
 5 [(False, 0, []), (True,  10, [4]), (False,  0, []),     (False,  0, [])]
10 [(False, 0, []), (True,  60, [0]), (True,  20, [4, 5]), (False,  0, [])]
15 [(False, 0, []), (False,  0, []),  (True,  70, [0, 4]), (False,  0, [])]
20 [(False, 0, []), (True,  50, [3]), (False,  0, []),     (True,  80, [0, 4, 5])]
25 [(False, 0, []), (False,  0, []),  (True,  60, [3, 4]), (False,  0, [])]
30 [(False, 0, []), (True, 120, [2]), (True, 110, [0, 3]), (True,  70, [3, 4, 5])]
35 [(False, 0, []), (False,  0, []),  (True, 130, [2, 4]), (True, 120, [0, 3, 4])]
40 [(False, 0, []), (False,  0, []),  (True, 150, [1, 3]), (True, 140, [2, 4, 5])]
45 [(False, 0, []), (False,  0, []),  (False,  0, []),     (True, 160, [1, 3, 4])]
50 [(False, 0, []), (False,  0, []),  (True, 170, [2, 3]), (True, 180, [2, 3, 4])]

最后一行显示 2 项的最低值为 170,3 项的最低值为 180。所以 170 是整体的最低值。

【讨论】:

  • 您能否说明您如何验证条目的有效性?以下``` K = [[(False, 0, []) for i in range(N + 1)] for j in range(W + 1)]; K[0][0] = (True, 0, [])```只给我第一个单元格是有效的,但我是从下往上循环的,所以我很困惑
  • @at8865 这两行看起来是正确的。它们后面应该是我在答案中输入的四行。那么接下来的两行是oldValid, oldValue, oldList = K[w][n]if oldValid:
【解决方案2】:

您的问题提到总价值最小,但其组合重量等于或超过容量重量。 但是经典背包选择总和永远不会超过背包容量的重量。因此,除非进行一些更改,否则经典版本将无法使用。

一种方法是使权重为负,那么权重列表将是: [-10, -20, -30, -20, -5, -5]

使容量也为负:-50

然后,如果您在此修改后的列表上应用经典算法,即选择总权重小于或等于容量且具有最小值的权重,您将选择值为 (3,4) 的项目170

【讨论】:

  • 这似乎很合乎逻辑,但不幸的是我无法通过将权重和容量变为负数得到除 0 以外的答案。
  • @at8865 实现起来有点棘手,但我认为应该可行。
猜你喜欢
  • 2022-10-04
  • 2022-01-18
  • 2023-03-11
  • 1970-01-01
  • 2019-05-23
  • 1970-01-01
  • 2017-04-12
  • 2021-03-23
  • 2011-11-18
相关资源
最近更新 更多