我遇到了以下问题陈述。
您有一个大小为
N
的自然数字列表,您必须将这些值分配到大小为A
的两个列表B
和N/2
中,以便平方和A
个元素的元素与B
元素的乘法最接近。示例: 考虑清单7 11 1 9 10 3 5 13 9 12.
优化的分布是:
清单A:5 9 9 12 13
清单B:1 3 7 10 11
导致差值为abs((5 + 9 + 9 + 12 + 13)^ 2 - (1 * 3 * 7 * 10 * 11))= 6
因此,您的程序应输出6,这是可以实现的最小差异。
我尝试了什么:
我尝试过贪婪的方法来解决这个问题。我采用了两个变量sum
和mul
。现在我开始逐个从给定的集合中获取元素,并尝试在变量和计算的当前值中添加它
和和乘法的平方。现在最终确定两个集合中的一个元素,以便组合给出最小可能值。
但是这种方法在给定的示例itselt中不起作用。我无法弄清楚这里可以使用什么方法。
我不要求提供解决方案的确切代码。任何可能的方法及其工作原因都没问题。
修改
答案 0 :(得分:1)
试试这个:
import java.util.Arrays;
public class Test {
public static void main(String [] args){
int [] arr = {7, 11, 1, 9, 10, 3, 5, 13, 9, 12};
int [][] res = combinations(5, arr);
int N = Arrays.stream(arr).reduce(1, (a, b) -> a * b);
int min = Integer.MAX_VALUE;
int [] opt = new int [5];
for (int [] i : res){
int k = (int) Math.abs( Math.pow(Arrays.stream(i).sum(), 2) - N/(Arrays.stream(i).reduce(1, (a, b) -> a * b)));
if(k < min){
min = k;
opt = i;
}
}
Arrays.sort(opt);
System.out.println("minimum difference is "+ min + " with the subset containing this elements " + Arrays.toString(opt));
}
// returns all k-sized subsets of a n-sized set
public static int[][] combinations(int k, int[] set) {
int c = (int) binomial(set.length, k);
int[][] res = new int[c][Math.max(0, k)];
int[] ind = k < 0 ? null : new int[k];
for (int i = 0; i < k; ++i) {
ind[i] = i;
}
for (int i = 0; i < c; ++i) {
for (int j = 0; j < k; ++j) {
res[i][j] = set[ind[j]];
}
int x = ind.length - 1;
boolean loop;
do {
loop = false;
ind[x] = ind[x] + 1;
if (ind[x] > set.length - (k - x)) {
--x;
loop = x >= 0;
} else {
for (int x1 = x + 1; x1 < ind.length; ++x1) {
ind[x1] = ind[x1 - 1] + 1;
}
}
} while (loop);
}
return res;
}
// returns n choose k;
// there are n choose k combinations without repetition and without observance of the sequence
//
private static long binomial(int n, int k) {
if (k < 0 || k > n) return 0;
if (k > n - k) {
k = n - k;
}
long c = 1;
for (int i = 1; i < k+1; ++i) {
c = c * (n - (k - i));
c = c / i;
}
return c;
}
}
从this stackoverflow answer获取的代码,另请参阅this维基百科关于组合的文章。
答案 1 :(得分:0)
我不确定在多项式时间内是否有任何精确解。但你可以尝试一种基于模拟退火的方法。
我的方法是:
贪婪的步骤:找到一个可以在优化错误的列表之间移动的元素。
随机步骤:从这两组中的任意一组中选择一个随机元素并计算错误。如果错误更好,请保留它。否则有q概率保持它。
在这两个步骤中的任何一个步骤中,确保尚未探索新状态(或至少不鼓励它)。
将p设置为较小的值(<0.1),q可能取决于误差差异。