从数组中选择元素的组合,其总和是最小可能的正数

时间:2013-06-27 17:10:18

标签: arrays algorithm data-structures selection mathematical-optimization

假设我有一个M元素数组,所有数字,负数或正数或零。

有人建议使用算法从数组中选择N元素,这样这些N元素的总和是可能的最小正数吗?

以此数组为例:

-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200

现在我必须选择任何5个元素,使得它们的和是可能的最小正数。

6 个答案:

答案 0 :(得分:6)

制剂

i = 1, ..., M

  • a_i成为候选人列表中的i号码
  • x_i表示i号码是否包含在N个所选号码

然后你想解决以下整数编程问题。

minimize: sum(a_i * x_i)
with respect to: x_i
subject to:
    (1) sum(a_i * x_i) >= 0
    (2) sum(x_i) = N
    (3) x_i in {0, 1}

您可以将“开箱即用”的整数程序求解器应用于此问题,以找到最佳解决方案或具有可控精度的次优解决方案。

资源

答案 1 :(得分:0)

我认为Kadane’s Algorithm可以做到这一点,虽然它是最大总和但我已经实现它以找到最小总和,但现在无法找到代码。

答案 2 :(得分:0)

如果你想找到最好的解决方案,你可以简单地使用蛮力即。尝试所有可能的fiwe数字组合。

像这样非常快速和肮脏的算法:

public List<Integer> findLeastPositivSum(List<Integer> numbers) {
    List<Integer> result;
    Integer resultSum;
    List<Integer> subresult, subresult2, subresult3, subresult4, subresult5;
    for (int i = 0; i < numbers.size() - 4; i++) {
        subresult = new ArrayList<Integer>();
        subresult.add(numbers.get(i));
        for (int j = i + 1; j < numbers.size() - 3; j++) {
            subresult2 = new ArrayList<Integer>(subresult);
            subresult2.add(j);
            for (int k = j + 1; k < numbers.size() - 2; k++) {
                subresult3 = new ArrayList<Integer>(subresult2);
                subresult3.add(k);
                for (int l = k + 1; l < numbers.size() - 1; l++) {
                    subresult4 = new ArrayList<Integer>(subresult3);
                    subresult4.add(k);
                    for (int m = l + 1; m < numbers.size(); m++) {
                        subresult5 = new ArrayList<Integer>(subresult4);
                        subresult5.add(k);
                        Integer subresultSum = sum(subresult5);
                        if (subresultSum > 0) {
                            if (result == null || resultSum > subresultSum) {
                                result = subresult;
                            }
                        }
                    }
                }
            }
        }
    }
    return result;
}

public Integer sum(List<Integer> list) {
    Integer result = 0;
    for (Integer integer : list) {
        result += integer;
    }
    return result;
}

这是一个非常快速和肮脏的算法,它可以做得更优雅。我可以提供清洁算法,例如使用递归。

它还可以进一步优化。例如。您可以从输入列表中删除类似的数字作为第一步。

答案 3 :(得分:0)

让初始数组已经短路了,或者我认为即使它没有短路也能正常工作。
N - >;阵列长度
M - &gt;元素要求。
R [] - &gt;答
TEMP [] - &gt;用于计算
minSum - &gt; minSum
A [] - &gt;初始输入

以上所有变量均为全局定义

int find(int A[],int start,int left)
{
    if(left=0)
    {
        //sum elements in TEMP[] and save it as curSum
        if(curSum<minSum)
        {
        minSum=curSum;
        //assign elements from TEMP[] to R[] (i.e. our answer)      
        }
    }

    for(i=start;i<=(N-left);i++)
    {
        if(left==M)
            curSum=0;
        TEMP[left-1]=A[i];
        find(A[],i+1,left-1);
    }
}

//匆匆忙忙,所以可能会出现一些错误..

ideone上的工作解决方案: http://ideone.com/YN8PeW

答案 4 :(得分:0)

这是Haskell中的次优化,(与我的许多想法一样)可能会进一步优化。它是这样的:

  1. 对数组进行排序(通过尝试升序和降序获得了有趣的结果)
  2. B N =数组的前N个元素
  3. B(i),对于i&gt; N =最佳候选人;其中(假设整数)如果它们都小于1,则候选人的总和的绝对值进行比较;如果他们都是1或更多,按他们的总和;如果只有一个候选人大于0,则选择该候选人。如果候选人的总和为1,则将该候选人作为答案。候选人是:
    B(i-1),B(i-1)[2,3,4..N] ++ array [i],B(i-1)[1,3,4..N] ++ array [i] ... B(i-1)[1,2..N-1] ++ array [i]
    B(i-2)[2,3,4..N] ++ array [i],B(i-2)[1,3,4..N] ++ array [i]。 ..B(i-2)[1,2..N-1] ++ array [i]
    ...
    B(N)[2,3,4..N] ++ array [i],B(N)[1,3,4..N] ++ array [i] ... B( N)[1,2..N-1] ++ array [i]
  4. 请注意,对于数组为负数(在升序排序的情况下)或正数(在降序排序的情况下)的数组部分,步骤3可以立即完成而无需计算。

    输出:

    *Main> least 5 "desc" [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
    (10,[-1000,600,300,100,10])
    (0.02 secs, 1106836 bytes)
    
    *Main> least 5 "asc" [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
    (50,[300,100,-200,-100,-50])
    (0.02 secs, 1097492 bytes)
    
    *Main> main -- 10000 random numbers ranging from -100000 to 100000
    (1,[-106,4,-40,74,69])
    (1.77 secs, 108964888 bytes)
    

    代码:

    import Data.Map (fromList, insert, (!))
    import Data.List (minimumBy,tails,sort)
    import Control.Monad.Random hiding (fromList)
    
    array = [-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200]
    
    least n rev arr = comb (fromList listStart) [fst (last listStart) + 1..m]
     where
      m = length arr
      r = if rev == "asc" then False else True
      sorted = (if r then reverse else id) (sort arr)
      listStart = if null lStart 
                     then [(n,(sum $ take n sorted,take n sorted))] 
                     else lStart
      lStart = zip [n..] 
             . takeWhile (all (if r then (>0) else (<0)) . snd) 
             . foldr (\a b -> let c = take n (drop a sorted) in (sum c,c) : b) [] 
             $ [0..]
      s = fromList (zip [1..] sorted)
      comb list [] = list ! m
      comb list (i:is)
        | fst (list ! (i-1)) == 1 = list ! (i-1)
        | otherwise              = comb updatedMap is 
       where updatedMap = insert i bestCandidate list 
             bestCandidate = comb' (list!(i - 1)) [i - 1,i - 2..n] where
               comb' best []     = best
               comb' best (j:js)
                 | fst best == 1 = best
                 | otherwise     =    
                     let s' = map (\x -> (sum x,x))
                            . (take n . map (take (n - 1)) . tails . cycle) 
                            $ snd (list!j)
                         t = s!i
                         candidate = minimumBy compare' (map (add t) s')
                     in comb' (minimumBy compare' [candidate,best]) js
      add x y@(a,b) = (x + a,x:b)
      compare' a@(a',_) b@(b',_) 
        | a' < 1    = if b' < 1 then compare (abs a') (abs b') else GT
        | otherwise = if b' < 1 then LT else compare a' b'
    
    rnd :: (RandomGen g) => Rand g Int
    rnd = getRandomR (-100000,100000)
    
    main = do
      values <- evalRandIO (sequence (replicate (10000) rnd))
      putStrLn (show $ least 5 "desc" values)
    

答案 5 :(得分:-2)

假设:M是原始数组

Pesudocode

 S = sort(M);
 R = [];
 sum = 0;
 for(i=0, i < length(S); i++){
   sum = sum + S[i];
   if(sum < 1){
     R.push(S[i]);
   }else{
     return R;
   }
 }