在实施与经典 0-1 背包类似情况的解决方案时重复项目

Posted

技术标签:

【中文标题】在实施与经典 0-1 背包类似情况的解决方案时重复项目【英文标题】:Repeating items when implementing a solution for a similar situation to the classic 0-1 knapsack 【发布时间】:2017-04-10 16:44:15 【问题描述】:

这个问题在很大程度上与经典的 0-1 背包问题相同,但有一些小的规则更改和一个大型数据集可供使用。

数据集(产品 ID、价格、长度、宽度、高度、重量): (20,000 行)

问题

一家公司即将交付第 100 万份订单。营销团队决定为下订单的客户提供奖品,以表示感谢。奖品是:幸运客户获得一个送货手提袋和1小时在仓库。利用这一小时将您想要的任何产品装满手提包,然后免费带回家。

规则

每项 1 个

组合体积

物品必须单独放入(尺寸应能放入手提包,例如 45 * 45 * 1 不适合)

组合产品的价值最大化

尽量减少抽签的重量

解决方案(使用动态规划):

from functools import reduce

# The main solver function
def Solver(myItems, myCapacity):

    dp = myCapacity: (0, (), 0)
    getKeys = dp.keys

    for i in range(len(myItems)):
        itemID, itemValue, itemVolume, itemWeight = myItems[i]
        for oldVolume in list(getKeys()):

            newVolume = oldVolume - itemVolume

            if newVolume >= 0:
                myValue, ListOfItems, myWeight = dp[oldVolume]
                node = (myValue + itemValue, ListOfItems + (itemID,), myWeight + itemWeight)
                if newVolume not in dp:
                    dp[newVolume] = node
                else:
                    currentValue, loi, currentWeight = dp[newVolume]
                    if currentValue < node[0] or (currentValue == node[0] and node[-1] < currentWeight):
                        dp[newVolume] = node

    return max(dp.values())

# Generate the product of all elements within a given list
def List_Multiply(myList):
    return reduce(lambda x, y: x * y, myList)

toteDims = [30, 35, 45]  
totalVolume = List_Multiply(toteDims)
productsList = []  

with open('products.csv', 'r') as myFile:
    for myLine in myFile:
        myData = [int(x) for x in myLine.strip().split(',')]
        itemDims = [myDim for myDim, maxDim in zip(sorted(myData[2:5]), toteDims) if myDim <= maxDim]
        if len(itemDims) == 3:
            productsList.append((myData[0], myData[1], List_Multiply(myData[2:5]), myData[5]))

print(Solver(productsList, totalVolume))

问题

输出给出重复项 IE。 (14018, (26, 40, 62, 64, 121, 121, 121, 152, 152), 13869)

我怎样才能纠正这个问题,让它只选择每个项目中的一项?

【问题讨论】:

【参考方案1】:

您的代码可能会产生带有重复项的答案的原因似乎是,在内部循环中,当您迭代到目前为止所有生成的卷时,代码可能已经替换了之前现有卷值的解决方案我们到了。

例如如果您的productsList 包含以下内容

productsList = [
    # id, value, volume, weight
    [1, 1, 2, 1],
    [2, 1, 3, 2],
    [3, 3, 5, 1]
]

totalVolume = 10

那么当你到达第三个项目时,dp.keys() 将包含:

10, 8, 7, 5

不保证迭代的顺序,但为了这个例子,我们假设它如上所示。然后dp[5] 将被包含项目#3 的新解决方案替换,在迭代的后期,我们将使用它作为新的、甚至更好的解决方案的基础(除了现在有重复项目)。

为了克服上述问题,您可以在迭代之前对键进行排序(按升序,这是默认值),例如for oldVolume in sorted(getKeys())。假设所有项目都有一个非负数,这应该保证我们在迭代之前永远不会替换 dp 中的解决方案。

我在上面看到的另一个可能的问题是我们最终使用max(dp.values()) 获得最佳解决方案的方式。在问题陈述中,它说我们希望在平局的情况下最小化权重。如果我没看错的话,元组的元素依次是 valuelist of itemsweight,所以下面我们' 与价值并列,但后一种选择会更可取,因为它的重量更小......但是max 返回第一个:

>>> max([(4, (2, 3), 3), (4, (1, 3), 2)])
(4, (2, 3), 3)

可以将排序键指定为max,这样可能会起作用:

>>> max([(4, (2, 3), 3), (4, (1, 3), 2)], key=lambda x: (x[0], -x[-1]))
(4, (1, 3), 2)

【讨论】:

for oldVolume in sorted(getKeys()) 是我需要的!

以上是关于在实施与经典 0-1 背包类似情况的解决方案时重复项目的主要内容,如果未能解决你的问题,请参考以下文章

0-1 背包,因体重不足和超重情况而受到处罚

无重复背包与有重复背包的不同应用

背包0-1背包与完全背包一维数组实现

数据结构与算法简记--动态规划

动态规划入门——经典的完全背包与多重背包问题

0-1背包问题如下,画用回溯法求解时的搜索情况,急用啊