如何分配桶中的元素数量以使其在一个范围内 - 算法

How to distribute the number of elements in a bucket so that it is Within a Range - Algorithm

我一直在解决一个问题,假设我有 50 个元素 n1, n2, n3, ... , n50.

现在我的桶数量有限,比如 5 个桶,桶可以容纳的范围从 100 到 150(这不过是那个桶中元素的总和),但不少于 100 , 不超过 150.

哪种算法最适合解决这个问题,使得5个桶都用完,元素(n1, n2, n3, ...)也用完

如果不使用桶或遗漏任何元素,则算法只是 returns "InvalidConditionsFound".

我试过 Knapsack,它给你一个接近给定数字的组合,但如何让它在一个范围内,并确保它明智地选择,这样所有的桶都被填满,而不是那两个桶满了 150 而另一个桶只有 50

一种方法是将其建模为整数程序。假设有 "m" 个数字 y_1、y_2、...、y_m 和 "n" 个存储桶。定义变量 x_ij,为您尝试分配的每个数字使用索引 "i",为每个存储桶使用索引 "j"。这些是二进制变量,指示每个数字是否分配给每个桶。

现在你有两组逻辑约束。首先,您需要将每个数字准确分配给一个桶。您可以通过为每个数字添加以下约束来做到这一点 "i":

x_i1 + x_i2 + ... + x_in = 1

你也有每个桶的范围限制"j":

100 <= y_1 x_1j + y_2 x_2j + ... + y_m x_mj <= 150

实际上你只是在寻找任何可行的解决方案,所以你可以将 objective 设置为 0 并将其视为可行性问题。

虽然您正在解决 NP 完全问题,因此这是一个理论上具有挑战性的练习,但您可能会发现现代优化软件可以解决您感兴趣的问题规模的问题。

为了了解可扩展性,请考虑以下使用 R 中的 lpSolve 包的实现;它 returns 当存在有效分配时从数字到桶的分配,否则 returns NA 值的向量:

library(lpSolve)
range.assign <- function(weights, n, min.sum, max.sum) {
  m <- length(weights)
  one.mat <- t(sapply(1:m, function(i) c(replicate(n, 1*((1:m) == i)))))
  w.mat <- t(sapply(1:n, function(j) c(rep(0, m*(j-1)), weights, rep(0, m*(n-j)))))
  mod <- lp(objective.in = rep(0, n*m),
            const.mat = rbind(one.mat, w.mat, w.mat),
            const.dir = rep(c("=", ">=", "<="), c(m, n, n)),
            const.rhs = rep(c(1, min.sum, max.sum), c(m, n, n)),
            all.bin=TRUE)
  if (mod$status == 0) {
    apply(matrix(mod$solution, nrow=m), 1, function(x) which(x >= 0.999))
  } else {
    rep(NA, m)
  }
}
range.assign(1:5, 2, 5, 10)
# [1] 1 1 1 1 2
range.assign(1:5, 2, 5, 6)
# [1] NA NA NA NA NA

我使用从 [1, 2, ..., 10] 中随机抽取的 m 个权重、桶的可接受范围 [100, 150] 和桶总数 n = ceiling(5.5*m / 125) 进行了测试。我看到了以下运行时缩放:

  • m = 100, n = 5: 0.1 秒
  • m = 200, n = 9: 0.6 秒
  • m = 300, n = 14: 2.2 秒
  • m = 400, n = 18: 16.9 秒

对于有十几个桶和几百个权重(以及这个权重向量结构)的问题,似乎可以使用免费求解器准确地解决这个问题。当然,您的复杂性结果表明它无法有效解决巨大的问题,但您可以解决您感兴趣的大小的实例。通过以下方式可以进一步优化:

  1. 使用 Gurobi 或 cplex 等商业求解器(两者通常都是非免费的,但对于学术用途是免费的)。
  2. 正在以稀疏格式输入约束矩阵。

听起来像是一个 k-partitioning 问题,通常被称为 bin-packing problem. 因为这个问题是 NP-Complete,所以不会有 'efficient' 解决方案。但是,如果您在互联网上四处看看,我相信您可以找到大量的代码示例,这样您和您的 (*咳咳*) "collegues" 就可以解决这个问题。