【问题标题】:How to to get selected items in Branch and Bound knapsack implementation in python?如何在python中的Branch and Bound背包实现中获取选定的项目?
【发布时间】:2021-08-21 08:01:33
【问题描述】:

我尝试了给定 here 的实现,使用 Branch and Bound 解决背包问题。

该解决方案看起来不错,但它并没有使最终选定的项目达到最佳值。 有没有办法通过最低限度地更改以下代码来实现这一点?

import sys

def bound(vw, v, w, idx):
    if idx >= len(vw) or w > limit:
        return -1
    else:
        while idx < len(vw) and w + vw[idx][1] <= limit:
            v, w, idx = v + vw[idx][0], w + vw[idx][1], idx + 1
        if idx < len(vw):
            v += (limit - w) * vw[idx][0] / (vw[idx][1] * 1.0)
        return v

def knapsack(vw, limit, curValue, curWeight, curIndex):
    global maxValue
    if bound(vw, curValue, curWeight, curIndex) >= maxValue:
        if curWeight + vw[curIndex][1] <= limit:
            maxValue = max(maxValue, curValue + vw[curIndex][0])
            knapsack(vw, limit, curValue + vw[curIndex][0], curWeight + vw[curIndex][1], curIndex + 1)

    if curIndex < len(vw) - 1:
        knapsack(vw, limit, curValue, curWeight, curIndex + 1)

    return maxValue

maxValue = 0

if __name__ == '__main__':
    with open(sys.argv[1] if len(sys.argv) > 1 else sys.exit(1)) as f:
        n, limit = map(int, f.readline().split())
        vw = []
        taken = n * [0]
        for ln in f.readlines():
            vl, wl = map(int, ln.split())
            vw.append([vl, wl, vl / (wl * 1.0)])
    print(knapsack(sorted(vw, key=lambda x: x[2], reverse=True), limit, 0, 0, 0))
    print(taken)

假设我们有一个包含以下内容的输入文件

4 11
8 4
15 8
4 3
10 5

我期待如下结果

19
0 1 1 0

我写了自己的implementation,它提供了上述所需的输出,但对于像one这样的大问题,它花费的时间太长了

【问题讨论】:

  • 限制在main中定义。
  • 那很难看。所以knapsack 得到了参数,bound 没有,但它们都使用相同的值。
  • 好吧,这就是 OP 所写的。它并没有给我带来太多困扰,因为只要它有效,这不是我关心的问题。

标签: python algorithm optimization knapsack-problem branch-and-bound


【解决方案1】:

我稍微重新组织了提供的代码,然后添加了逻辑以跟踪最佳选择。由于您希望该选择是一个零和一的列表,因此我为其使用了一个位图(一个大整数),并且每个项目都在该位图中分配了一个位。

这是它的外观:

from collections import namedtuple

Item = namedtuple("Item", "value, weight, bit")

def knapsack(items, limit):
    maxValue = 0
    bestTaken = 0
    
    def bound(value, weight, index):
        if index >= len(items) or weight > limit:
            return -1
        else:
            item = items[index]
            while weight + item.weight <= limit:
                value, weight, index = value + item.value, weight + item.weight, index + 1
                if index >= len(items):
                    return value
                item = items[index]
            else:
                return value + (limit - weight) * item.value / item.weight

    def recur(taken, value, weight, index):
        nonlocal maxValue, bestTaken

        if maxValue < value:
            maxValue = value
            bestTaken = taken

        if index < len(items) and bound(value, weight, index) >= maxValue:
            item = items[index]
            if weight + item.weight <= limit:
                recur(taken | item.bit, value + item.value, weight + item.weight, index + 1)
            recur(taken, value, weight, index + 1)

    # Add bit mask for each item:
    items = [Item(*item, 1 << index) for index, item in enumerate(items)]
    items.sort(key=lambda item: -item.value / item.weight)
    recur(0, 0, 0, 0)
    return maxValue, ('{:0{width}b}').format(bestTaken, width=len(items))[::-1]

if __name__ == '__main__':
    # Demo input
    s = """4 11
           8 4
           15 8
           4 3
           10 5"""

    lines = s.splitlines(False)
    _, limit = map(int, lines.pop(0).split())
    items = [tuple(map(int, line.split())) for line in lines]
    value, bits = knapsack(items, limit)
    print("Maximised value:", value)
    print("Item selection:", bits)

【讨论】:

  • 我不太了解位部分,但它有效。但问题是它不能解决 10000 个项目的问题,因为需要很大的 int。有其他解决方案吗?
  • 嗯,背包问题不能在多项式时间内解决。从您的问题中,我了解到您对此算法感到满意,并且只需要它来返回所选项目。如果现在您还有另一个问题——关于所选算法的性能,那么问题就不同了。我帮不了你。
  • 好的。假设在短时间内解决了一个大问题以获得最佳解决方案而不返回所选项目,那么识别所选项目是否会显着增加求解时间。我假设它不会。在这种情况下,位图解决方案受到问题大小的限制,不是因为它难以解决,而是因为我们无法分配足够大的位。这就是为什么我要求另一种解决方案。但无论如何感谢您的解决方案。
  • 不,我测试了您问题中的代码,并在此处提供了修改后的代码。效率没有显着差异。当然,有一点点开销,但与整体执行时间相比,它是微不足道的。
  • 回到您的第一条评论。您可以使用集合而不是大整数,或者只使用包含所选项目的列表。在算法结束时,您可以将这样的列表转换为您想要的 0-1 列表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多