改进的背包算法
Posted
技术标签:
【中文标题】改进的背包算法【英文标题】:Modified Knapsack algorithm 【发布时间】:2014-03-28 11:07:20 【问题描述】:我有不同类别的物品。每个项目都有value
和weight
。
例如:
ClassA: [A1, A2, A3]
ClassB: [B1, B2, B3]
ClassC: [C1, C2, C3]
我应该如何修改经典的 0-1 背包问题,以便算法优化解决方案以最大化整体价值,考虑类中的所有项目但允许从一个类中最多选择一项?
package knapsack;
import java.util.ArrayList;
import java.util.List;
public class Knapsack
private int totalNumberOfItems;
private int maxmimumKnapsackCapacityUnits;
private double[][] optimum;
private boolean[][] solution;
private double [] value;
private int [] weight;
public Knapsack(int knapsackCapacityUnits, List<KnapsackItem> items)
this.totalNumberOfItems = items.size();
this.maxmimumKnapsackCapacityUnits = knapsackCapacityUnits;
this.optimum = new double[items.size() + 1][knapsackCapacityUnits + 1];
this.solution = new boolean[items.size() + 1][knapsackCapacityUnits + 1];
this.value = new double[items.size() + 1];
this.weight = new int[items.size() + 1];
int index=1;
for(KnapsackItem item : items)
value[index] = item.value;
weight[index] = item.weight;
index++;
public List<KnapsackItem> optimize()
for(int currentItem = 1; currentItem <= totalNumberOfItems; currentItem++)
for(int currentWeightUnit = 1; currentWeightUnit <= maxmimumKnapsackCapacityUnits; currentWeightUnit++)
double option1 = optimum[currentItem - 1][currentWeightUnit];
double option2 = Integer.MIN_VALUE;
if(weight[currentItem] <= currentWeightUnit)
option2 = value[currentItem] + optimum[currentItem-1][currentWeightUnit - weight[currentItem]];
optimum[currentItem][currentWeightUnit] = Math.max(option1, option2);
solution[currentItem][currentWeightUnit] = (option2 > option1);
boolean take [] = new boolean[totalNumberOfItems + 1];
for(int currentItem = totalNumberOfItems,
currentWeightUnit = maxmimumKnapsackCapacityUnits;
currentItem > 0; currentItem --)
if(solution[currentItem][currentWeightUnit])
take[currentItem] = true;
currentWeightUnit = currentWeightUnit - weight[currentItem];
else
take[currentItem] = false;
List<KnapsackItem> items = new ArrayList<KnapsackItem>();
for(int i = 0; i < take.length; i++)
KnapsackItem newItem = new KnapsackItem();
newItem.value = value[i];
newItem.weight = weight[i];
newItem.isTaken = take[i];
items.add(newItem);
return items;
谢谢!
【问题讨论】:
【参考方案1】:经典算法是这样的:
for i in items:
for w in possible total weights (downwards):
if w is achievable with maximum value v:
(w + weight[i]) is also achievable with at least value (v + value[i])
这里的方法将是一个轻微的变化:
for c in classes:
for w in possible total weights (downwards):
if w is achievable with maximum value v:
for i in items of class c:
(w + weight[i]) is also achievable with at least value (v + value[i])
使用您的代码,更改将如下:
也许您会想要为每个类制作一个单独的项目列表。根据目前所做的,我希望 value
和 weight
成为列表列表,并且一些变量和数组命名为 numberOfClasses
和 numberOfClassItems
来监视新列表的长度。
例如,假设两个 1 类项目是 (w=2,v=3) 和 (w=3,v=5),三个 2 类项目是 (w=1,v=1), (w=4, v=1) 和 (w=1,v=4)。然后我们将有:totalNumberOfItems = 5
,numberOfClasses = 2
,numberOfClassItems = [2, 3]
,value = [[3, 5], [1, 1, 4]]
和weight = [[2, 3], [1, 4, 1]]
。
也就是说,如果您从0
索引。像您一样从1
索引将在每个列表的开头留下未使用的零或空列表。
for (currentItem)
循环将变为for (currentClass)
循环。数组optimum
和solution
将被currentClass
索引而不是currentItem
。
option2
的值实际上将被计算为几个选项中的最佳值,每个类项一个:
double option2 = Integer.MIN_VALUE;
for (currentItem = 1; currentItem <= numberOfClassItems[currentClass];
currentItem++)
if(weight[currentClass][currentItem] <= currentWeightUnit)
option2 = Math.max (option2, value[currentClass][currentItem] +
optimum[currentClass - 1][currentWeightUnit -
weight[currentClass][currentItem]]);
option1
,不要使用此类的任何物品。
【讨论】:
感谢您的回复!如果我在您修改后的算法版本中理解正确,您正在检查项目的重量与最大容量if w is achievable with maximum value v:
但这发生在您迭代类项目之前。所以我不确定这应该如何工作。我已经包含了我的代码,所以也许我的算法有点不同,你可以看看我做了什么。谢谢!
@WildGoat:我添加了一些关于这种方法可能对您的代码产生什么影响的指针。
非常感谢,我正在尝试了解您的解决方案并更改我现有的代码。我会让你知道进展如何。感谢您如此建设性的回答!【参考方案2】:
解决此问题的方法是将类 A,B,C
视为项目本身,然后继续在各个类中进行选择以从中选出最好的。
number of items = number of classes = N
Knapsack capacity = W
Let item[i][k] be kth item of ith class.
简单的 DP 解决方案通过对背包解决方案的简单修改:-
int DP[n][W+1]=0;
//base condition for item = 0
for(int i=0;i<=W;i++)
for(int j=0;j<item[0].size();j++)
if(i>=item[0][j].weight)
DP[0][i] = max(DP[0][i],item[0][j].value);
//Actual DP
for(int k=0;k<=W;k++)
for(int i=0;i<n;i++)
for(int j=0;j<item[i].size();j++)
if(k>=item[i][j].weight)
DP[i][k] = max(DP[i][k],DP[i-1][k-item[i][j].weight]+item[i][j].value);
DP[i][k] = max(DP[i][k],DP[i-1][k]);
print("max value : ",DP[n-1][W]);
【讨论】:
这应该如何工作?对于初学者来说,一个类中没有单一的“最佳”项目。例如,一个类可能包含一个权重为 3 和值 5 的项,以及另一个权重为 5 和值 8 的项。当您的容量有限时,您不会事先知道哪个“更好”。 @Gassa 这是一个 DP,我们不会提前决定,而是从一个类中单独选择每个项目并检查结果并选择为特定子问题提供最大权重的项目。它类似于原始的背包问题,但只考虑一个类中的一项或不考虑一项。你能告诉我你没有从伪代码中得到什么 抱歉,我发现文本(“从类中选择最佳项目”)令人困惑:它读起来就像“每个类中只有一个最佳项目”。所以,我没有过多考虑代码。现在我阅读了代码,似乎做对了。 @VikramBhat,你对这个有什么想法吗 - math.stackexchange.com/questions/2415617?谢谢。以上是关于改进的背包算法的主要内容,如果未能解决你的问题,请参考以下文章