01 背包专业化

Posted

技术标签:

【中文标题】01 背包专业化【英文标题】:01 Knapsack specialization 【发布时间】:2013-12-27 22:21:17 【问题描述】:

对不起,如果这个问题已经得到解答,但我对算法没有深入的了解,也并不总是注意到算法的不同专业之间的微妙之处。我有(我认为是)01-Knapsack 问题的一个轻微变体。我有一个最大重量为 W 的背包,有 N 个重量为 w 且值为 v 的物品可供选择。我想要做的是在不超过 W 的情况下最大化总价值 V。

经典背包。

这里是转折:在这些项目中,我需要确保我有一定的数量(不是最多,而是准确金额)取自不同的类别。

假设我们有类别

F - 食品 T - 玩具 C - 衣服 M - 其他(F、T 或 C)

我要进行 2 天的旅行,所以我需要带 2 件食物、1 个逗孩子玩的玩具和 2 件衣服。作为一个踢球者,我可以多带一个 F、T 或 C 项目。请注意,每个项目都是独一无二的,只能包含一次。

从我发现的所有算法来看,它似乎是 01(唯一物品)和有界变体的混合体,尽管在经典的有界背包中,我们绑定了我们可以包含特定物品的次数 vs特定的类别

如果有人能指出正确的算法,我将不胜感激。以“正常”语言编写代码的加分点,如果实现允许我查看前 n 个最佳结果,则加分(你知道,如果最佳解决方案包括我真的无法忍受或拥有的玩具) 2 套相互冲突的服装)。

编辑:请注意,我最终希望能够进行更长时间的旅行,所以我打算总共带 8-10 件物品,类别最多可以有 250 件左右的物品(那个孩子的玩具太多了)。我可以做一些优化来减少每个类别中的一些项(我真的不打算穿丑陋的夏威夷衬衫),但我不能将其减少到足以产生直接蛮力的程度实施可行。

【问题讨论】:

如果我没看错的话,你有一个带有多个子背包问题的整体背包问题。听起来像优化算法方法可能会首先消除不可能的解决方案,因为您的解决方案空间非常小。 不考虑物理卷怎么办? 什么是 ILP 求解器? 什么是“普通”语言? @user1146334 ILP = integer linear programming 【参考方案1】:

一种可能的 ILP/LP 公式(最明显的公式,但绝不仅仅是一种公式)可能是:(未测试)

maximize sum(v[i] * x[i])
subject to:
0 <= x[i] <= 1        // can take every item at most once
sum(w[i] * x[i]) <= W // don't go over the weight limit
F <= sum(f[i] * x[i]) <= F + 1 // take F or F+1 food items
T <= sum(t[i] * x[i]) <= T + 1 // take T or T+1 toys
C <= sum(c[i] * x[i]) <= C + 1 // take C or C+1 clothes
sum(x[i]) == F + T + C + M     // take exactly the right number of items

v[i] 是值,w[i] 是重量,f[i] 是物品的“食物”,t[i] 是“玩具”,现在您知道c[i] 是什么了。属于一个以上类别的物品计为双倍或三倍(即,如果您拿了一个可食用玩具,它将计入玩具和食物),可以通过放入该物品的多个副本来避免这种情况,每个副本一份其类别中的副本,其中每个副本仅属于一个类别。

但现在真正的问题是,如何解决?这仍然是一个积极研究的领域,但这里有一个想法在这种情况下应该很有效。

带有分支和界限。使用线性松弛绑定(使用线性规划解决上述问题,允许决策变量x[i] 是分数),对于这个问题,它应该给出一个相当不错的边界(并且可以接受,它总是会给出一个具有至少与解决 ILP 问题一样高的目标值)。在非整数变量上进行分支。

【讨论】:

这可能是我的误解,但上面似乎不起作用。对于 F/T/C 条目,我想获取确切数量的条目。看起来像是一个简单的修复方法,使其 sum(f[i] * x[i]) == 2 等。但这个等式不是总结了 F 中选择的价值吗?如果 f[i] = 123,那会抛出等式。此外,这似乎是假设所有项目都在 x[i] 向量中。这是没有意义的,因为这些组是不重叠的(M 除外)。 @user1146334 这个想法是f[i] 指示项目i 是否是食品项目(1) 或不是(0)。 x[i] 表示您是否接受项目i。因此,您将它们相乘并相加,就得到了您正在服用的食物总数。 啊,有道理。 @user1146334 如果您只想使用一次 RingPop,您当然可以添加一个约束:RingPop_f + RingPop_t &lt;= 1。为避免与 M 重复,请不要包含 M(只需获取更多项目,就像我展示的那样) @user1146334 是的,对于属于多个类别的每个项目,您都需要一个约束,以确保您只能接受一次【参考方案2】:

您可以简单地暴力破解所有选项。

这是一些 Python 代码示例:

from itertools import combinations
F=['apple','banana','clementine']
T=['compass','telephone']
C=['socks','gloves','hat']
weights='apple':1,'banana':2,'clementine':3,'compass':4,'telephone':5,
       'socks':6,'gloves':7,'hat':8
values='apple':10,'banana':9,'clementine':8,'compass':7,'telephone':6,
       'socks':5,'gloves':4,'hat':3
choices=[]
W=45  # Maximum allowed weight
n=5   # Number of choices to display
for M in range(3):  # M represents which category gets an extra item
    num_food = 3 if M==0 else 2
    num_toys = 2 if M==1 else 1
    num_clothes = 3 if M==2 else 2
    for food_to_take in combinations(F,num_food):
        for toys_to_take in combinations(T,num_toys):
            for clothes_to_take in combinations(C,num_clothes):
                things_to_take = food_to_take+toys_to_take+clothes_to_take
                weight = sum(weights[a] for a in things_to_take)
                value = sum(values[a] for a in things_to_take)
                if weight<=W:
                    choices.append([value,weight,things_to_take])
for value,weight,things_to_take in sorted(choices,reverse=True)[:n]:
    print value,weight,things_to_take

打印

43 23 ('apple', 'banana', 'clementine', 'compass', 'socks', 'gloves')
42 24 ('apple', 'banana', 'clementine', 'telephone', 'socks', 'gloves')
42 24 ('apple', 'banana', 'clementine', 'compass', 'socks', 'hat')
41 25 ('apple', 'banana', 'compass', 'telephone', 'socks', 'gloves')
41 25 ('apple', 'banana', 'clementine', 'telephone', 'socks', 'hat')

【讨论】:

我不认为暴力破解是一种选择。最终我想去长途旅行,所以我打算带 8 或 9 件物品,有些类别有 200 件左右的物品可供选择。【参考方案3】:

我认为基于模拟退火的方法可以很好地工作; SA 已广泛应用于 KP,并且可以很简单地为解决方案空间添加额外的约束。基本策略如下:

    从初始可接受的解决方案开始,该解决方案由每个类别中必要数量的最低重量项目组成。 在每一轮中,假设用该类别中的另一项替换特定类别中的一项。 如果假设的候选解不可接受,请转到 6。 根据候选解决方案相对于当前解决方案总值的总值和退火计划计算候选解决方案的接受概率。 接受具有计算概率的候选解。如果被接受,则替换当前解决方案;如果不被接受,则当前解决方案不变。 如果已达到退火轮的总数,则退出。否则,提前退火计划并转到 2。

对于条件不是特别差的问题,SA 在背包问题上做得很好,所以我认为它可能适合您的用例。有关 SA、退火时间表和计算接受概率的介绍性材料在网络上广泛提供。

【讨论】:

我不知道你刚才说了什么,但听起来很有趣。看来是时候学习新东西了!【参考方案4】:

通过执行以下操作,使用遗传算法在合理的时间内找到一个好的解决方案:-

    在染色体中为每种类型所需的数量保留插槽。例如:-

    食物:牛奶、苹果 玩具:等。 ......

    从有效的解决方案开始,例如所有类型的最小权重。

    尝试更改项目的值并检查其是否有效。找到它们之间的交叉点,例如交换类型。

    评估具有最大值的最佳染色体。

    这样做直到最好的染色体在固定的迭代次数下保持不变。

【讨论】:

以上是关于01 背包专业化的主要内容,如果未能解决你的问题,请参考以下文章

20162306 陈是奇 预备作业01

寒假作业01

单独的 .cpp 文件中的模板类专业化

用于函数对和模板专业化的 RAII 包装器

计算机专业Java毕业设计(项目+论文+源码)

旗图片 / “包”治百病 —— Vertx战术背包