如何找到刚刚超过阈值的元素组合

时间:2015-10-06 16:32:43

标签: algorithm optimization mathematical-optimization genetic-algorithm knapsack-problem

我有一个问题陈述说:如果你有一个元素数组{x1,x2,x3,... x10},找到元素的组合,使它只是总和超过一个阈值(比如阈值)价值是100)。

因此,如果存在x2+x5+x8 = 105x3+x5+x8=103x4+x5 = 101等组合,则算法应输出X4,X5。

背包算法发出的值接近但在阈值的较小一侧(此处为100)。我想要相反的,即所选元素的最小总和大于100。

是否有任何算法集或任何可能解决此问题的算法的特殊情况?

2 个答案:

答案 0 :(得分:1)

我首先要注意你要求的最小值严格大于某个目标。通常"严格大于"并且"严格低于"约束比"大于或等于"更难。或者"小于或等于"限制。如果你有所有整数值,那么你可以简单地翻译你的约束"总和超过100"到"总和大于或等于101"。我假设你已经为问题的其余部分做了这样的转变。

一种方法是将此视为整数优化问题,其中每个数字的二元决策变量y_i是否包含它。然后我们的目标是最小化数字的总和,可以建模为:

min x_1*y_1 + x_2*y_2 + ... + x_n*y_n

这种情况下的约束是数字之和至少为100:

x_1*y_1 + x_2*y_2 + ... + x_n*y_n >= 100

一般来说,这是一个难题(请注意,它至少与子集和问题一样难,这是NP完全的)。但是,现代优化求解器可能对您的问题实例足够有效。

要测试此问题的自由求解器的可伸缩性,请考虑使用R中的lpSolve包进行以下实现(如果问题可行则返回选定的子集,否则返回NA):< / p>

library(lpSolve)
min.subset <- function(x, min.sum) {
  mod <- lp("min", x, matrix(x, nrow=1), ">=", min.sum, all.bin=TRUE)
  if (mod$status == 0) {
    which(mod$solution >= 0.999)
  } else {
    NA
  }
}
min.subset(1:10, 43.5)
# [1] 2 3 4 5 6 7 8 9
min.subset(1:10, 88)
# [1] NA

为了测试可扩展性,我将从n中随机选择[1, 2, ..., 1000]个元素,将目标总和设置为元素总和的一半。运行时间是:

  • 使用n=100,它在0.01秒内运行
  • 使用n=1000,它在0.1秒内运行
  • 使用n=10000,它以8.7秒的速度运行

看起来你可以解决超过10k个元素(使用所选分布)的这个问题而没有太多的计算挑战。如果你的问题对于我在这里使用过的免费求解器来说太大了,你可以考虑使用Gurobi或cplex,这两个商业解决方案可以免费学习,但不是免费的。

答案 1 :(得分:1)

假设X是所有x_i的总和。然后等效地,您要求的x_i的最小子集最多总计X - 100(因为这些x_i的补充将是您问题的最佳解决方案)。因此,所有背包理论都可以在这里应用。

在实践中(非常大的实例),我建议使用this形式的Nemhauser-Ullman泛化,它可以解决数百万个对象的实例。