假设我有一个M
元素数组,所有数字,负数或正数或零。
谁能建议一种算法N
从数组中选择元素,使这些N
元素的总和是最小的可能正数?
以这个数组为例:
-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200
现在我必须选择任何 5 个元素,使它们的总和是可能的最小正数。
假设我有一个M
元素数组,所有数字,负数或正数或零。
谁能建议一种算法N
从数组中选择元素,使这些N
元素的总和是最小的可能正数?
以这个数组为例:
-1000,-700,-400,-200,-100,-50,10,100,300,600,800,1200
现在我必须选择任何 5 个元素,使它们的总和是可能的最小正数。
对于i = 1, ..., M
:
a_i
成为i
您的候选人名单中的第 th 个数字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}
您可以将“开箱即用”的整数程序求解器应用于此问题,以找到具有可控精度的最优解或次优解。
我想Kadane 的算法可以解决问题,虽然它是为了最大和,但我也实现了它来找到最小和,虽然现在找不到代码。
让初始数组已经被短路,或者我想即使它没有被短路也会起作用..
N -> 数组 M 的长度
-> 元素 req。
R[] -> 回答
TEMP[] -> 用于计算
minSum -> minSum
A[] -> 初始输入
以上所有变量都是全局定义的
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
这是 Haskell 中的一些次优,(就像我的许多想法一样)可能会得到进一步和更好的优化。它是这样的:
请注意,对于数组中数字为负数(升序排序的情况下)或正数(降序排序的情况下)的部分,可以立即执行第 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)
如果你想找到最好的解决方案,你可以简单地使用蛮力,即。尝试所有可能的数字组合。
像这种非常快速和肮脏的算法:
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;
}
这是一个非常快速和肮脏的算法,它可以做得更优雅。我可以提供更简洁的算法,例如使用递归。
它还可以进一步优化。例如,您可以从输入列表中删除相似的数字作为第一步。
假设:M是原始数组
伪代码
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;
}
}