您的问题是众所周知的Knapsack problem,并且没有任何已知的有效算法可以解决它。
您可能正在寻找递归解决方案:
static int naive_knapSack(int[] v, int[] w, int size, int index) {
// no more items
if (index >= v.length)
return 0;
// we cannot use the current item
if (size < w[index])
return naive_knapSack(v, w, size, index + 1);
// the maximum value will be taking in account the current index OR not
return Math.max(
naive_knapSack(v, w, size, index + 1), // ignoring this item
v[index] + naive_knapSack(v, w, size - w[index], index + 1) // using this item
);
}
但是,如果背包大小适合内存(例如小于 4G)存在一个动态编程解决方案 O(n^2) 成本:
static int knapSack(int[] v, int[] w, int size) {
int[][] m = new int[w.length + 1][size + 1];
for (int i = 1; i <= w.length; i++)
for (int j = 0; j <= size; j++)
m[i][j] = w[i - 1] > j ? m[i - 1][j] : Math.max(m[i - 1][j], m[i - 1][j - w[i - 1]] + v[i - 1]);
return m[w.length][size];
}
在这方面基本相同,但递归的每个选项都是记忆的(只计算一次)。如果你记忆前面的递归函数,你会得到相同的结果。
我们可以比较两个输出
int[] v = new int[]{50, 10, 30, 40};
int[] w = new int[]{20, 5, 10, 30};
// specific cases
System.out.println(knapSack(v, w, 25));
System.out.println(knapSack(v, w, 30));
System.out.println(naive_knapSack(v, w, 25, 0));
System.out.println(naive_knapSack(v, w, 30, 0));
结果相同(注意第一个解不是50是60)
60
80
60
80
但是,当动态规划算法是多项式时,递归算法的效率是指数的,仅使用 34 项进行比较
// efficiency
int ITEMS = 34;
int[] V = IntStream.range(0, ITEMS).map(i -> ThreadLocalRandom.current().nextInt(1, 50)).toArray();
int[] W = IntStream.range(0, ITEMS).map(i -> ThreadLocalRandom.current().nextInt(50, 150)).toArray();
int itemsWeight = Arrays.stream(W).sum();
int capacity = ThreadLocalRandom.current().nextInt(itemsWeight >> 2, itemsWeight >> 1);
long t0 = System.nanoTime();
int r1 = naive_knapSack(V, W, capacity, 0);
long t1 = System.nanoTime();
int r2 = knapSack(V, W, capacity);
long t2 = System.nanoTime();
System.out.printf("r1 = %d, t1 = %f%nr2 = %d, t2 = %f%n", r1, (t1 - t0) * 1e-9, r2, (t2 - t1) * 1e-9);
我们得到
r1 = 481, t1 = 11,11 seconds
r2 = 481, t2 = 0,004 seconds