【问题标题】:Find combination of numbers such that their addition is closest to a number? [closed]找到数字的组合,使得它们的加法最接近一个数字? [关闭]
【发布时间】:2019-01-12 20:01:55
【问题描述】:

我有一个名为 Orders 的列。我想以这样的方式将它们聚类成组,使集群中的订单总和接近 300。以下是输入。

**Orders**
100
198
50
40
215
296

输出应该是这样的

Orders  Group
100     1
198     1
50      2
40      2
215     2
296     3

这只是一个示例数据。在 Real 中,数据非常庞大。这可以使用 R 来完成吗?

【问题讨论】:

标签: r excel


【解决方案1】:

结果

以下是解决问题的函数,虽然这里是结果

find_grouping(orders, 300L)
#      orders group
# [1,]    100     1
# [2,]    198     1
# [3,]     50     2
# [4,]     40     2
# [5,]    215     2
# [6,]    296     3

allocate_groups(orders, 300L, 3L)    # third argument <-> max. num. of groups
#      orders group
# [1,]    100     3
# [2,]    198     3
# [3,]     50     2
# [4,]     40     2
# [5,]    215     2
# [6,]    296     1 

# bigger vector
set.seed(123)
orders <- sample(1:300, 15)
find_grouping(orders, 300L)
#       orders group
#  [1,]     87     2
#  [2,]    236     2
#  [3,]    122     3
#  [4,]    263     4
#  [5,]    279     5
#  [6,]     14     9
#  [7,]    156     6
#  [8,]    262     7
#  [9,]    162     8
# [10,]    133     8
# [11,]    278     9
# [12,]    132    10
# [13,]    196     1
# [14,]    165    10
# [15,]     30     7
allocate_groups(orders, 300L, 3L)
#       orders group
#  [1,]     87     1
#  [2,]    236     2
#  [3,]    122     3
#  [4,]    263     3
#  [5,]    279     1
#  [6,]     14     2
#  [7,]    156     3
#  [8,]    262     3
#  [9,]    162     2
# [10,]    133     1
# [11,]    278     2
# [12,]    132     2
# [13,]    196     1
# [14,]    165     1
# [15,]     30     3

使用数据orders = c(100L, 198L, 50L, 40L, 215L, 296L)


编辑:新功能

考虑到要指定组数的附加约束,这里来了一个新功能

create_groups <- function (orders, num, group_num) {
  orders
  groups <- rep(list(NA_integer_), group_num)
  for (k in sort(orders, decreasing = TRUE)) {
    sums <- vapply(1:group_num, function (s) as.integer(sum(groups[[s]], na.rm = TRUE)), integer(1))
    index <- ifelse(any(sums + k <= num), which(sums + k <= num)[which.min(abs(sums[which(sums + k <= num)]+k - num))], NA_integer_)
    index <- ifelse(is.na(index), which.min(sums), index)
    groups[[index]] <- append(groups[[index]],k)
    groups[[index]] <- groups[[index]][!is.na(groups[[index]])]
  }
  groups
}
allocate_groups <- function (orders, num, group_num) {
  groups <- create_groups(orders, num, group_num)
  g <- rep(seq_along(groups), sapply(groups, length))
  out <- cbind(orders, group = g[match(orders, unlist(groups))])
  out
}
# results above

添加的约束实际上使问题变得更简单了:我们想用orders 填充(最多)n 个抽屉,并且任何总和都应该尽可能接近num


函数

这里是函数的完整代码

find_grouping <- function (orders, num) {
    combs2 <- RcppAlgos::comboGeneral(orders, 2L, constraintFun = 'sum')
    combs2 <- cbind.data.frame(combs2,close=abs(num - combs2[,3]))
    out <- integer(length(orders))
    skip <- NA_integer_
    group <- 1L
    for (k in seq_along(out)) {
      val1 <- orders[k]
      if (val1 %in% skip) next
      ind1 <- (.subset2(combs2,1L) == val1) | (.subset2(combs2,2L) == val1)  
      ind2 <- (which.min(.subset2(combs2, 4L)[ind1]))
      ind3 <- which(ind1)[ind2]
      val2 <- .subset2(combs2, 3L)[ind3]
      if (abs(num-val1) <= abs(num-val2)) {
        out[k] <- group
        group  <- group + 1L
        next
      }
      intList <- as.integer(combs2[ind3,1:2])
      ordersRemain <- setdiff(orders, intList)
      if (abs(num-val2) <= abs(num-val2-min(ordersRemain))) {
        skip <- c(skip, intList)
        out[orders %in% intList] <- group
        group <- group + 1
        next
      }
      val3 <- val2
      cond <- FALSE
      while (!cond) {
        toAdd <- which.min(abs(num - (val2 + ordersRemain)))
        val3 <- val3 + ordersRemain[toAdd]
        intList <- c(intList, ordersRemain[toAdd])
        ordersRemain <- ordersRemain[-toAdd]
        cond <- abs(num-val3) <= abs(num-val2-min(ordersRemain))
      }
      skip <- c(skip, intList)
      out[orders %in% intList] <- group
      group <- group + 1
    }
    cbind(orders,group=out)
}

说明

第一步是生成所有(2 个)订单组合 使用RcppAlgos::comboGeneral(这是一个相当快的方法)

# num
combs <- RcppAlgos::comboGeneral(orders, 2L, constraintFun = 'sum')
combs <- cbind.data.frame(combs,close=abs(num - combs[,3])) # check how far from num are the combinations
#      1   2   3 close
# 1  100 198 298     2
# 2  100  50 150   150
# 3  100  40 140   160
# 4  100 215 315    15
# ...

从现在开始有几种方法。我选择了一个循环,在每次迭代中,我找到当前值orders[k] 的最佳组合(即最接近num),然后记住给定组合(例如100;198)并为组合分配group 值。

【讨论】:

  • 这行得通,但它将数据划分为真实数据中的无限组。我们可以对组进行检查吗?例如,在这种情况下,如果我们希望最多创建 3 个组。
  • @NitinKansal 查看添加约束的编辑。
  • 这可以工作并划分数据,但问题是我希望所有组的订单数量几乎相同。假设总共有 8323 个订单必须分配给 26 个团队,那么每个团队将获得大约 320 个订单。 I have a sample order data: Orders: 236,233,170,127,129,102,115,142,155,144,135,144,138,86,153,99,107,111,120,100,106,175,101,116,159,111,137,152,158,198,122,163,133,146,146,111,127,82,95,174,78,155,91,154,95,145,172,102,45,89,85,57,72,84,35,65,90,72,61,95,96,125 ,51,49,5,8,23,26,81,14,39,35,24,57,95,136,53,53,53
【解决方案2】:

这解决了您提出的问题的变体,其中组总和可能不超过目标总和。

library(BBmisc); library(dplyr);
bin.capacity <- 305
df <- data.frame(Orders = c(100,198,50,40,215,296)) %>%
  mutate(Group = BBmisc::binPack(Orders,bin.capacity))
> df
  Orders Group
1    100     3
2    198     3
3     50     2
4     40     2
5    215     2
6    296     1

对于 bin.capacity = 300:

> df
  Orders Group
1    100     3
2    198     3
3     50     2
4     40     4
5    215     2
6    296     1

【讨论】:

    猜你喜欢
    • 2012-06-02
    • 1970-01-01
    • 2019-01-16
    • 2023-03-20
    • 1970-01-01
    • 2019-07-11
    • 1970-01-01
    • 1970-01-01
    • 2012-10-25
    相关资源
    最近更新 更多