【问题标题】:How to choose elements that sum up to n?如何选择总和为n的元素?
【发布时间】:2015-10-26 03:33:49
【问题描述】:

假设我有以下数据框

x <- c("p1","p2","p3","p4","p5","p6","p7","p8","p9","p10")
y <- c(1,4,3,5,5,7,2,2,6,8)
df <- data.frame(x,y)

x 代表球员,y 代表进球。我想要所有目标总和为 10 的球员子集,比如说

{p1,p3,p9},{p3,p6},{p7,p8,p9}...

【问题讨论】:

  • 不确定这是否有帮助,但它似乎相关:stackoverflow.com/q/31572497/1191259 您可能想要强加团队规模以保持可行(仅适用于 10 名球员,但如果你有另外 20 名球员 0或1个目标,它会变得丑陋)。 ...还有另一个:stackoverflow.com/q/32855755/1191259
  • 顺便说一句,您的cbind 电话完全是多余的。别管它。

标签: r


【解决方案1】:

1) lpSolve 这可以使用整数线性规划来完成。我们使用目标 c(0, ..., 0) 和由 y 组成的单行矩阵作为约束矩阵。约束的右手边必须等于 n,即 10。

library(lpSolve)
y <- c(1,4,3,5,5,7,2,2,6,8)
n <- length(y)
k <- sum(cumsum(sort(y)) <= n) + 1 # upper bound to no of players in group
out <- lp(objective = numeric(n), 
   const.mat = matrix(y, 1), const.dir = "==", const.rhs = n,
   all.bin = TRUE, num.bin.solns = sum(choose(n, 1:k)))

# solution vector seems to have junk at end so truncate it and reshape to matrix
soln <- matrix(head(out$solution, n * out$num.bin.solns), n)

一共找到了19个解决方案:

> out
Success: the objective function is 0 
         19 solutions returned

> out$num.bin.solns
[1] 19

> dim(soln)
[1] 10 19

soln 的列是可行的解决方案。例如,第一个解决方案是玩家 1、2 和 4:

> soln[, 1]
 [1] 1 1 0 1 0 0 0 0 0 0
> which(soln[, 1]==1)
[1] 1 2 4

我们可以像这样以字符串的形式列出解决方案:

> x <- c("p1","p2","p3","p4","p5","p6","p7","p8","p9","p10")
> apply(soln == 1, 2, function(v) toString(x[v]))
 [1] "p1, p2, p4"     "p4, p5"         "p3, p4, p7"     "p1, p4, p7, p8"
 [5] "p1, p2, p3, p8" "p1, p2, p3, p7" "p1, p3, p9"     "p3, p4, p8"    
 [9] "p1, p2, p5"     "p3, p5, p7"     "p2, p9"         "p3, p5, p8"    
[13] "p3, p6"         "p1, p5, p7, p8" "p1, p6, p7"     "p1, p6, p8"    
[17] "p7, p8, p9"     "p8, p10"        "p7, p10"  

2) wle 第二种方法是创建 1:10 的所有 10^2 个子集作为二元向量 v,然后选择 y %*% v == 10(其中 y 来自问题)。这种方法可以生成简洁的代码,只要y 不太长就可以。

library(wle)
m <- sapply(0:(2^10-1), function(x) binary(x, 10)$binary)
soln2 <- m[, y %*% m == 10]

使用与 (1) 相同的方法将其转换为字符串向量(如果首选该形式)。

更新:一些更正和改进并添加 (2)。

【讨论】:

  • 非常感谢您的回答...我可以设置团队中的最低人数吗?说最少 3 名玩家
  • 将解中的玩家人数限制为k=3:k
【解决方案2】:

您可以使用这种蛮力方法: 结果是:

 [1] "p2,p9"       "p3,p6"       "p4,p5"       "p7,p10"      "p8,p10"      "p1,p2,p4"    "p1,p2,p5"   
 [8] "p1,p3,p9"    "p1,p6,p7"    "p1,p6,p8"    "p3,p4,p7"    "p3,p4,p8"    "p3,p5,p7"    "p3,p5,p8"   
[15] "p7,p8,p9"    "p1,p2,p3,p7" "p1,p2,p3,p8" "p1,p4,p7,p8" "p1,p5,p7,p8"

数据:

x<-c("p1","p2","p3","p4","p5","p6","p7","p8","p9","p10")
y<-c(1,4,3,5,5,7,2,2,6,8)
df<-data.frame(x=x,y=y, stringsAsFactors = FALSE)
df$id <- seq_len(nrow(df)) # Adding an ID column

获取最多max_comb 元素的所有可能组合

max_comb <- nrow(df)
my_combn <- function(m, x){
  combn(x, m, simplify = FALSE)
}
dat <- lapply(1:max_comb, my_combn, df$id)

为选择的组合设置名称

combn_names <- function(ind, vec, collapse = ", "){
  paste(vec[ind], collapse = collapse)
}

set_list_combn_names <- function(l, vec){
  setNames(l, lapply(l, combn_names, vec = vec))
}
dat <- lapply(dat, set_list_combn_names, df$x)

检查 sum 是否等于x=10 并输出组合名称

sum_equal_x <- function(ind, vec, x){
  sum(vec[ind]) == x
}
names(which(unlist(lapply(dat, lapply, sum_equal_x, df$y, 10))))

结果:

> names(which(unlist(lapply(dat, lapply, sum_equal_x, df$y, 10))))
 [1] "p2,p9"       "p3,p6"       "p4,p5"       "p7,p10"      "p8,p10"      "p1,p2,p4"    "p1,p2,p5"   
 [8] "p1,p3,p9"    "p1,p6,p7"    "p1,p6,p8"    "p3,p4,p7"    "p3,p4,p8"    "p3,p5,p7"    "p3,p5,p8"   
[15] "p7,p8,p9"    "p1,p2,p3,p7" "p1,p2,p3,p8" "p1,p4,p7,p8" "p1,p5,p7,p8"

【讨论】:

    【解决方案3】:

    你可以这样做:

    require(utils)
    
    x<-c("p1","p2","p3","p4","p5","p6","p7","p8","p9","p10")
    y<-c(1,4,3,5,5,7,2,2,6,8)
    df<-data.frame(cbind(x=x,y=y))
    
    search.val <- 10
    max.num    <- length(x)
    
    all.comb <- lapply(1:max.num, function(n){ combn(x,n) })
    
    # Calcualte sum
    #   Foreach combination length 1:n
    sums <- lapply(all.comb, function(comb.mat){
      # Foreach combination of length n
      apply(comb.mat,2,function(col){
        sum(as.numeric( df[which(df$x %in% col),]$y ))
      })
    })
    
    # Find which combinations have sum 10
    vals <- lapply(1:max.num,function(i){
      sum.vect <- sums[[i]]
      inds     <- which(sum.vect == search.val)
      lapply(inds, function(j){
        all.comb[[i]][,j]
      })
    })
    
    sum.of.10 <- unlist(vals,recursive=FALSE)
    

    这是一个蛮力解决方案,其中所有组合的所有总和直到长度 max.num 都是使用 combn 函数计算的。

    【讨论】:

      猜你喜欢
      • 2012-07-26
      • 1970-01-01
      • 1970-01-01
      • 2015-05-30
      • 1970-01-01
      • 2011-10-07
      • 2013-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多