将数组划分为相等的大小,以使给定函数的值最小

时间:2017-06-12 14:34:06

标签: java algorithm

我遇到了以下问题陈述。

  

您有一个大小为N的自然数字列表,您必须将这些值分配到大小为A的两个列表BN/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,这是可以实现的最小差异。

我尝试了什么:

我尝试过贪婪的方法来解决这个问题。我采用了两个变量summul。现在我开始逐个从给定的集合中获取元素,并尝试在变量和计算的当前值中添加它 和和乘法的平方。现在最终确定两个集合中的一个元素,以便组合给出最小可能值。

但是这种方法在给定的示例itselt中不起作用。我无法弄清楚这里可以使用什么方法。

要求提供解决方案的确切代码。任何可能的方法及其工作原因都没问题。

修改

来源:CodinGame, Community puzzle

2 个答案:

答案 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)

我不确定在多项式时间内是否有任何精确解。但你可以尝试一种基于模拟退火的方法。

我的方法是:

  1. 将listA和listB初始化为随机状态
  2. 概率p运行贪婪步骤,否则运行随机步骤
  3. 跟踪状态和相应的错误(使用HashMap)
  4. 贪婪的步骤:找到一个可以在优化错误的列表之间移动的元素。

    随机步骤:从这两组中的任意一组中选择一个随机元素并计算错误。如果错误更好,请保留它。否则有q概率保持它。

    在这两个步骤中的任何一个步骤中,确保尚未探索新状态(或至少不鼓励它)。

    将p设置为较小的值(<0.1),q可能取决于误差差异。