我正在尝试解决一个经典的背包问题,容量大到30.000.000并且它运行良好直到20.000.000然后它耗尽了内存:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
我试图将所有值和容量除以1.000.000,但这会产生浮点数,我不认为这是正确的方法。我也试图制作类型长的数组和矩阵,但这没有帮助。 也许是另一种数据结构? 任何指针都欢迎......
代码:
public class Knapsack {
public static void main(String[] args) {
int N = Integer.parseInt(args[0]); // number of items
int W = Integer.parseInt(args[1]); // maximum weight of knapsack
int[] profit = new int[N+1];
int[] weight = new int[N+1];
// generate random instance, items 1..N
for (int n = 1; n <= N; n++) {
profit[n] = (int) (Math.random() * 1000000);
weight[n] = (int) (Math.random() * W);
}
// opt[n][w] = max profit of packing items 1..n with weight limit w
// sol[n][w] = does opt solution to pack items 1..n with weight limit w include item n?
int[][] opt = new int[N+1][W+1];
boolean[][] sol = new boolean[N+1][W+1];
for (int n = 1; n <= N; n++) {
for (int w = 1; w <= W; w++) {
// don't take item n
int option1 = opt[n-1][w];
// take item n
int option2 = Integer.MIN_VALUE;
if (weight[n] <= w) option2 = profit[n] + opt[n-1][w-weight[n]];
// select better of two options
opt[n][w] = Math.max(option1, option2);
sol[n][w] = (option2 > option1);
}
}
// determine which items to take
boolean[] take = new boolean[N+1];
for (int n = N, w = W; n > 0; n--) {
if (sol[n][w]) { take[n] = true; w = w - weight[n]; }
else { take[n] = false; }
}
// print results
System.out.println("item" + "\t" + "profit" + "\t" + "weight" + "\t" + "take");
for (int n = 1; n <= N; n++) {
System.out.println(n + "\t" + profit[n] + "\t" + weight[n] + "\t" + take[n]);
}
//Copyright © 2000–2011, Robert Sedgewick and Kevin Wayne. Last updated: Wed Feb 9 //09:20:16 EST 2011.
}
答案 0 :(得分:2)
以下是我用过的一些技巧。
首先,稀疏矩阵的变体。它并不是非常稀疏,但是假设它们与之前的条目相同,而不是假设“非存储条目”为零。这可以在任一方向(在容量的方向上或在物品的方向上)工作,而不是(容易地)同时在两个方向上。好的伎俩,但不能打败两个方向都很大的实例。
其次,动态编程和Branch&amp; amp;界。首先,使用只有“最后两行”的DP。这为您提供了最佳解决方案的价值。然后使用Branch&amp;绑定以找到与最佳解决方案对应的项的子集。按value/weight
排序,将放宽value[next_item] * (capacity_left / weight[next_item])
应用于绑定。提前了解最佳值会使修剪变得非常有效。
“最后两行”是指“上一行”(包含最多i
所有项目的解决方案的表格的一部分)和“当前行”(您正在填写的权利)现在)。它可能看起来像这样,例如:(这是C#btw,但应该很容易移植)
int[] row0 = new int[capacity + 1], row1 = new int[capacity + 1];
for (int i = 0; i < weights.Length; i++)
{
for (int j = 0; j < row1.Length; j++)
{
int value_without_this_item = row1[j];
if (j >= weights[i])
row0[j] = Math.Max(value_without_this_item,
row1[j - weights[i]] + values[i]);
else
row0[j] = value_without_this_item;
}
// swap rows
int[] t = row1;
row1 = row0;
row0 = t;
}
int optimal_value = row1[capacity];
答案 1 :(得分:0)
使用递归方法来解决问题。有关详细信息,请参阅http://penguin.ewu.edu/~trolfe/Knapsack01/Knapsack01.html。
希望它会有所帮助。
答案 2 :(得分:-2)
将你的for循环分解为方法调用。
一旦方法本身完成,这将使局部变量GC成为可能。
因此,在同一个main方法中,不是嵌套for循环,而是调用具有相同功能的方法,然后调用第二个方法,并且有效地将代码分解为小的局部变量包,这些变量可以在超出范围时收集