我有一组浮点值,我想将它们分成两组,其大小最多相差一个元素。另外,两组之间的值和之差应该是最小的。可选地,如果元素的数量是奇数并且总和不能相等,则较小的集合应该具有较大的总和。
这将是最佳解决方案,但我真的只需要对子集大小约束的精确解决方案。总和的差异并不一定非常小,但应该接近。如果较小的集合(如果有的话)具有更大的总和,我也更愿意。
我意识到这可能与partition problem有关,但它不完全相同或严格。
我目前的算法如下,但我想知道是否有办法改进:
arbitrarily divide the set into two sets of the same size (or 1 element size difference)
do
diffOfSums := sum1 - sum2
foundBetter := false
betterDiff := 0.0
foreach pair of elements from set1 and set2 do
if |diffOfSums - 2 * betterDiff| > |diffOfSums - 2 * (value1 - value2)| then
foundBetter := true
betterDiff := value1 - value2
endif
done
if foundBetter then swap the found elements
while foundBetter
我对这种方法的问题是我不确定实际的复杂性以及是否可以改进。它肯定不能满足将较小的子集留下较大总和的要求。
是否有任何现有算法碰巧做我想要实现的目标?如果没有,你能否建议我改进我的算法或弄清楚它可能已经合理地解决了这个问题?
答案 0 :(得分:3)
很容易证明分区问题在多项式时间内减少了这个问题。
想象一下,你想要解决一些阵列A的分区,但你只知道如何解决你的问题。你只需要加倍数组长度,用零填充它。如果您可以使用算法解决它,那么您已经解决了分区问题。这证明你的问题是NP难的。
但是你会看到你无法将这个问题减少到分区(即它不是NP完全),除非你限制你的花车的精度。在这种情况下,相同的算法将解决这两个问题。
在一般情况下,你能做的最好的就是回溯。
答案 1 :(得分:2)
我的建议是对值进行排序,然后考虑每对值(v1,v2),(v3,v4)将每对中的一个元素放入一个分区。
这个想法是交替将值放入每个集合中,所以:
s1 = {v1, v4, v5, v8, . . . }
s2 = {v2, v3, v6, v7, . . . }
如果元素数量奇数,请将最后一个值放入最符合条件的集合中。
你有一个轻松的minimal定义,所以不需要完整的搜索。对于许多值的分布,上述内容应该可以很好地工作。