假设我有一个包含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的变体)。
答案 0 :(得分:1)
首先对数组进行排序,然后将最大数字放入负数组,并将第二大数字放入正数组。 将最大数字设置为正数组,直到它们的总和大于零。 现在设置另一个负数。重复它直到你设置5负。这是贪心算法。 似乎你的问题是np-complete,它看起来像AST问题,但你的问题的大小限制为10,所以你可以通过强力搜索解决它,你只需要检查C(10,5)< 10 ^ 5对于今天的PC,这个数字很小。
答案 1 :(得分:1)
以下是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
*主> minCombi [2,4,2,6,9,10,1,7,6,3] (0,[ - 7,-10,-2,-4,-2,1,9,6,6,3])
答案 2 :(得分:1)
这是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);
}
}
答案 3 :(得分:0)
您是否尝试过计算差异?即:取第一个数字。找到具有最小差异的值,并求和。继续直到完成。在最坏的情况下,算法是O(n ^ 2)复杂度,这不是完全可取的,但它是一个起点
答案 4 :(得分:0)
欢迎来到NP级问题的世界!
您可以通过强力计算或尝试轻松的方法(如单纯形算法)来计算最优解,这将为您提供平均情况下复杂性的平均时间解决方案