Algorithm 给定N个整数的绝对值,求和最接近0的N/2个负值和N/2个正值的组合

Algorithm 给定N个整数的绝对值,求和最接近0的N/2个负值和N/2个正值的组合,algorithm,integer,sum,permutation,Algorithm,Integer,Sum,Permutation,假设我有一个由10个数字组成的数组,其绝对值范围可以从1到10。值可以重复。这方面的一个例子可能是 {2, 4, 2, 6, 9, 10, 1, 7, 6, 3}. 对于这些数字中的每一个,我们可以指定一个正号或负号,但是在每个组合中应该总是有5个负数和5个正数,例如 {-2, -4, 2, -6, 9, 10, 1, 7, -6, -3} {2, -4, -2, 6, -9, 10, -1, 7, -6, 3} 是遵循此规则的可能排列 我想找出,在给定集合的所有可能的半正值和半负值排列中

假设我有一个由10个数字组成的数组,其绝对值范围可以从1到10。值可以重复。这方面的一个例子可能是

{2, 4, 2, 6, 9, 10, 1, 7, 6, 3}. 
对于这些数字中的每一个,我们可以指定一个正号或负号,但是在每个组合中应该总是有5个负数和5个正数,例如

{-2, -4, 2, -6, 9, 10, 1, 7, -6, -3}
{2, -4, -2, 6, -9, 10, -1, 7, -6, 3}
是遵循此规则的可能排列

我想找出,在给定集合的所有可能的半正值和半负值排列中,其值最接近于0的最小正值或负值和


有什么建议吗?我觉得这个问题在计算上非常复杂,我不确定是否有一个解决方案不是暴力解决方案(例如,枚举所有置换,然后应用3Sum最接近的变量)。

您是否尝试过计算差异?Ie:坐第一个号码。找到差值最小的值,然后求和。继续,直到完成。在最坏的情况下,算法是O(n^2)复杂度,这并不理想,但它是一个起点

欢迎来到NP类问题的世界


您可以通过bruteforce或尝试一种宽松的方法(如单纯形算法)来计算最优解,该方法将在多项式时间内根据平均事例复杂度计算出解

首先对数组进行排序,然后将最大数放入负组,将第二大数放入正组。 将一个最大值设置为正组,直到它们的和大于零。 现在设置另一个负数。重复此操作,直到将5设置为负数。这是贪婪算法。
似乎您的问题是np完全问题,看起来像AST问题,但问题的大小限制为10,所以您可以通过蛮力搜索解决它,您只需检查C(10,5)以下是Haskell中的一个示例,列出并比较了所有126种可能的组合:

import Data.List
import Data.Ord

{-code by Will Ness-}
divide :: [a] -> [([a], [a])]
divide [] = [([],[])]
divide (x:xs) = go ([x],[],xs,1,length xs) where
  go (a,b,[],i,j) = [(a,b)]
  go (a,b, s@(x:xs),i,j) 
     | i>=j = [(a,b++s)]
     | i>0  = go (x:a, b, xs, i+1, j-1) ++ go (a, x:b, xs, i-1, j-1)
     | i==0 = go (x:a, b, xs,   1, j-1) ++ go (x:b, a, xs,   1, j-1)  

{-code by groovy-}       
minCombi list = 
  let groups = map (\x -> map (negate) (fst x) ++ snd x) (divide list)
      sums = zip (map (abs . sum) groups) groups
  in minimumBy (comparing fst) sums
*Main>minCombi[2,4,2,6,9,10,1,7,6,3]

(0,[-7,-10,-2,-4,-2,1,9,6,6,3])

这是amin k描述的算法的java实现

它没有Haskell实现那么酷,我没有正式的证据证明它在每种情况下都有效,但它似乎是有效的

import java.util.Arrays;
import java.util.Random;

public class TestPermutations {

int[] values = new int[10];
int[] positives = new int[5];
int[] negatives = new int[5];

public static void main(String... args) {
    new TestPermutations();
}

public TestPermutations() {
    Random ra = new Random();
    System.out.println("generating sequence...");
    for (int i = 0; i < 10; i++) {
        values[i] = (ra.nextInt(10) + 1);
        System.out.print(values[i] + " ");
    }
    Arrays.sort(values);

    int sum = 0;
    int positiveIndex = 0;
    int negativeIndex = 0;
    for (int i = values.length - 1; i >= 0; i--) {
        if (i == values.length - 1) {
            negatives[negativeIndex] = - values[i];
            negativeIndex++;
            sum -= values[i];
        }
        else {
            if (sum <= 0) {
                if (positiveIndex < 5) {
                    positives[positiveIndex] = values[i];
                    positiveIndex++;
                    sum += values[i];
                }
                else {
                    negatives[negativeIndex] = - values[i];
                    negativeIndex++;
                    sum -= values[i];
                }
            }
            else {
                if (negativeIndex < 5) {
                    negatives[negativeIndex] = - values[i];
                    negativeIndex++;
                    sum -= values[i];
                }
                else {
                    positives[positiveIndex] = values[i];
                    positiveIndex++;
                    sum += values[i];
                }
            }
        }
    }

    System.out.print("\npositives ");
    for (int pos : positives) {
        System.out.print(pos + " ");
    }
    System.out.print("\nnegatives ");
    for (int neg : negatives) {
        System.out.print(neg + " ");
    }
    System.out.println("\nsum closest to 0: " + sum);
}
}
导入java.util.array;
导入java.util.Random;
公共类测试置换{
int[]值=新的int[10];
int[]正数=新的int[5];
int[]否定=新的int[5];
公共静态void main(字符串…参数){
新的TestPermutations();
}
公共测试置换(){
随机ra=新随机();
System.out.println(“生成序列…”);
对于(int i=0;i<10;i++){
数值[i]=(ra.nextInt(10)+1);
System.out.print(值[i]+“”);
}
数组。排序(值);
整数和=0;
int正指数=0;
int negativeIndex=0;
对于(int i=values.length-1;i>=0;i--){
if(i==values.length-1){
否定[negativeIndex]=-值[i];
negativeIndex++;
总和-=数值[i];
}
否则{

if(sum)您是否尝试过计算差异?即:取第一个数字。找到差异最小的值,然后求和。继续,直到完成。在最坏的情况下,算法为O(n^2)复杂度,这并不完全令人满意,但它是一个起点。我将把它作为一个潜在的答案发布。如果这有帮助,那么你可以将它标记为正确的。这是一种贪婪的方法,不太适合那个问题,你将解决局部最小值(次优解决方案)我试图在纸上遵循你的算法,但我不确定我是否完全理解。找到下一个差值最小的数字的最佳方法是在迭代之前按升序对数组进行排序。那么我到底应该求和什么?数字?还是差值?@G\G,也许你能提供一个更有效的解决方案?等等G\G在你看到一个可能更好的解决方案之前,没有必要看我的。我想你指的是NP完全。NP包括P。如果NP!=P,NP完全意味着不在P中的问题。如果你想说它是NP完全,你应该激发它。是的,NP类又名NP完全。这个问题是众所周知的背包问题的变种。!=(看右边的图片)它可能是背包的一种变体,但并不清楚,所以你应该说它是如何简化的。我猜你想说的是,由于库克定理以及它与布尔可满足性问题的相似性,这是一个NP完全问题,因此没有解决方案可以在多项式时间内解决它。你错了家庭作业,这是我试图设计的一个拼图的一小部分。谢谢。如果你只想把壁橱设为0,你可以对数组进行排序,(A1+A3+A5+A7+A9)-(A2+A4+A6+A8+A10)或-(A1+A3+A5+A7+A9)+(A2+A4+A6+A8+A10)是最小的???我不这么认为。取这组数字:{1,2,2,2,3,3,4,7,9}。如果我计算(1+2+4+9)-(2+10)结果是-5。但我知道一种情况,当最小和为-1时,例如(-1+2+2-2+3-3+4-7-9+10)排序后和为-1。设置-A10(最大值),然后尝试用+A9填充这个洞,…。填充时(它们的总和>0).你可以设置一个新的负片,5张底片,我对哈斯克尔语言一无所知,但我会深入研究。。。