01背包专业化

时间:2013-12-27 22:21:18

标签: algorithm knapsack-problem

对不起,如果已经回答了这个问题,但我对算法知之甚少,并且并不总是注意算法的不同专业之间的细微差别。我(我认为)是01-Knapsack问题的一个小变种。我有一个最大重量为W的背包,并且有N个项目可供选择,它们具有重量w和值v。我想要做的是最大化总值V,而不超过W.

经典背包。

这是扭曲:在这些项目中,我需要确保我有一定金额(不是最多,但 完全 金额)来自不同类别。

所以我们假设我们有类别

  • F - 食品
  • T - Toys
  • C - 衣服
  • M - 其他(F,T或C)

我要去2天旅行,所以我需要带2件食品,1件玩具逗孩子,2件衣服。作为一个踢球者,我可以再拿一个F,T或C项。注意每个项目都是唯一的,只能包含一次。

从我发现的所有algos中,它似乎是01(独特物品)和有界变体的混合体,但在经典的有界背包中,我们约束了我们可以包含特定物品的次数vs特定的类别

如果有人能指出我正确的算法,那将非常感激。使用“普通”语言的代码的加分点,如果实现允许我查看前n个最佳结果的额外分数(你知道,如果最佳解决方案包括我刚刚REALY的玩具无法忍受或具有两件相互冲突的装备。)

编辑:请注意,我希望最终可以进行长途旅行,因此我总共需要花费8-10个项目,而且这些类别最多可以包含250个项目(这个孩子的玩具太多了) )。我可以做一些优化来减少每个类别中的某些项目(我真的不会去拿丑陋的夏威夷衬衫),但是我无法减少它以使一个直接的蛮力实施可行。

4 个答案:

答案 0 :(得分:3)

一种可能的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]是“玩具”,现在你知道{ {1}}将会。属于多个类别的物品数量增加一倍或三倍(即,如果你带一个可食用的玩具,它会计入玩具和朝向食品),这可以通过放入该物品的多个副本来避免,每个物品一个其类别,副本每个只有一个类别。

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

分支和绑定。使用线性松弛进行约束(使用线性规划解决上述问题,允许决策变量c[i]成为分数),对于这个问题,这应该给出相当不错的界限(并且可以接受,它将始终提供解决方案)目标值至少与解决ILP问题一样高。)对不是整数的变量进行分支。

答案 1 :(得分:1)

您可以简单地强制所有选项。

以下是一些示例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')

答案 2 :(得分:1)

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

  1. 从最初的可接受解决方案开始,该解决方案包含每个类别中必需数量最低权重的项目。
  2. 在每一轮中,假设将特定类别中的一个项目替换为该类别中的另一个项目。
  3. 如果假设的候选解决方案不被允许,请转到6.
  4. 根据候选解决方案相对于当前解决方案总价值和退火计划的总价值计算候选解决方案的接受概率。
  5. 以计算的概率接受候选解。如果它被接受,它将取代当前的解决方案;如果不被接受,则当前解决方案不变。
  6. 如果已达到退火轮次总数,请退出。否则,推进退火计划并转到2.
  7. 对于那些条件不是特别差的问题,SA在背包问题上做得很好,所以我认为你的用例可能会很好。关于SA的介绍材料,退火时间表和计算接受概率,可在网上广泛获得。

答案 3 :(得分:0)

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

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

    食物:牛奶,苹果  玩具:等..  ......

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

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

  4. 用最大值评估最佳染色体。

  5. 这样做直到最佳染色体保持不变为固定的迭代次数。