【问题标题】:How to split by a group with constraints如何按具有约束的组进行拆分
【发布时间】:2019-05-20 21:57:26
【问题描述】:

我正在尝试解决一个优化问题。

例如,我们有 10 组人:

第 1 组:20

第 2 组:5 人

第 3 组:15 人

第 4 组:10 人

第 5 组:12 人

第 6 组:26

第 7 组:41

第 8 组:15

第 9 组:69

第 10 组:9

我们正在尝试将这些人群装载到多辆公共汽车上,但不能破坏该人群。每辆巴士只能载80人。有关 r 代码/功能的任何建议,以使我获得所需的最少公共汽车数量。谢谢!

【问题讨论】:

  • 这是一个“分区”问题。在 SO 和 Web 中搜索关键字“partition”的解决方案。我怀疑有几个问题是重复的。

标签: r mathematical-optimization


【解决方案1】:

对于固定数量的容器,这可以表述为整数线性规划问题。如果 g 是 10 个组号的向量,cap 是容量,即 80,则必须至少有 k=ceiling(sum(g) / cap) 容器且不超过 k=length(g) 。因此,我们从最小数量到最大数量的容器依次适应,一旦我们适应就停止。对于问题中显示的数据(见最后的注释),只需要一次迭代。

lp 问题中,有 k × n 个二进制变量,如果我们将它们排列在 k × n 矩阵中,那么如果第 i 个容器包含元素 j,则第 i,j-th 为 1,否则为 0。

lp 问题有 k+n 个约束。前 k 个约束将每个 k 个容器的总和约束为 cap,其余的 n 约束确保每个元素只能出现在一个容器中。

library(lpSolve)
stopifnot(all(g <= cap))

n <- length(g)
kmin <- ceiling(sum(g) / cap)
for(k in seq(kmin, n)) {
  objective.in <- rep(1, k * n)
  const.mat <- rbind(diag(k) %x% t(g), t(rep(1, k)) %x% diag(n))
  const.dir <- c(rep("<=", k), rep("==", n))
  const.rhs <- c(rep(cap, k), rep(1, n))
  res <- lp("min", objective.in, const.mat, const.dir, const.rhs, all.bin = TRUE)
  if (res$status == 0) break
}

# iterations
k - kmin + 1
## [1] 1

# solution - each col is an element, each row is a container 
matrix(res$solution, k, byrow = TRUE)
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,]    1    1    1    1    1    0    0    1    0     0
## [2,]    0    0    0    0    0    1    1    0    0     1
## [3,]    0    0    0    0    0    0    0    0    1     0

注意

使用的输入是这样的:

Lines <- "Group 1: 20
Group 2: 5
Group 3: 15
Group 4: 10
Group 5: 12
Group 6: 26
Group 7: 41
Group 8: 15
Group 9: 69
Group 10: 9"
g <- read.table(text = Lines)$V3

cap <- 80

【讨论】:

  • 谢谢你,G. Grothendieck!效果很好!我实际上将在另一个项目中使用线性编程。这是完美的!
【解决方案2】:

这是一个装箱问题,您可以在网上找到很多方法。

无论如何,这里有一个贪心算法的解决方案。这并不能保证最佳解决方案,但应该足够体面 -

grp <- c(20, 5, 15, 10, 12, 26, 41, 15, 69, 9)
cap <- 80

sorted_grp <- sort(grp, decreasing = T)

buses <- list("bus_1" = NULL)
for(g in seq_along(sorted_grp)) {
  balance_cap <- cap - sapply(buses, function(x) sum(x))
  if(any(balance_cap >= sorted_grp[g])) {
    feasible_bus <- which(balance_cap >= sorted_grp[g])[1]
    buses[[paste0("bus_", feasible_bus)]] <- c(buses[[paste0("bus_", feasible_bus)]], sorted_grp[g])
  } else {
    buses[[paste0("bus_", length(buses) + 1)]] <- sorted_grp[g]
  }
}

# > paste0("Number of buses: ", length(buses))
# [1] "Number of buses: 3"
# > buses
# $`bus_1`
# [1] 69 10
# 
# $bus_2
# [1] 41 26 12
# 
# $bus_3
# [1] 20 15 15  9  5

【讨论】:

  • 非常感谢您的及时回复,Shree!效果很好! +1。不幸的是,我的投票只被记录下来,但不要更改公开显示的帖子分数,因为我的声誉低于 15。您的解决方案简洁优雅!也感谢您指出我进一步的参考(装箱问题)。
猜你喜欢
  • 1970-01-01
  • 2016-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-05
  • 1970-01-01
  • 2018-05-03
  • 1970-01-01
相关资源
最近更新 更多