R - 如何根据数据框中其他列的总和进行最大化

R - How to maximise based of sum of other column from data frame

所以我创建了一个数据框,代表一支梦幻球队,其中每个位置 (a,b,c) 都有给定的得分 (points) 和价格 (price):

library(reshape2)
x<-102 #num players

replicate(x/3,paste(letters[1:3]))->l
l<-melt(l)
l<-l[,3]
l<-data.frame(l)

# pnt<- as.integer(runif(90,min=-4, max=30))
pnt<-pmax(round(as.numeric(rnorm(x,mean=6,sd=4)),digits=0),-3)
prc<-pmax(sort(round(as.numeric(rnorm(x,mean=6,sd=3)),digits=1)),4.5)

df1<-as.data.frame(pnt)
df2<-as.data.frame(prc)
df<-data.frame(df1,df2,l)
rownames(df) <- seq(from=1,to=x)
colnames(df)<-c("points","price","pos")

p<-df[rev(order(df$pos)),]
a<-p[((2*x/3)+1):x,1:3]
a<-a[rev(order(a$points)),]

我现在只使用我的数据框 'a',看起来像:

 price points pos
1   7    14     a
2   8    12     a
3   3    8      a
4   10   7      a

我正在尝试 select 通过组合 4 名玩家(由每一行表示)可能获得的最大分数。通常这很容易,只需按点排序数据框,select 前 4 个。 但是我想对 28 位的四位玩家施加最高价格限制。(这是一个任意数字来显示问题) 这可能会排除前 4 名玩家,并可能允许该价格限制下的最高点数不连续(按点数顺序)。

你对如何处理这件事有什么建议吗? 我尝试了以下但它只允许连续点 selected.

z<-integer()
y<-integer()
for(i in 1:31){
  j<-i+2
  x<-sum(a[i:j,]$point)
  xx<-sum(a[i:j,]$price)
  y<-c(y,x)
  z<-c(z,xx)
  yz<-data.frame(y,z)
}
yz

#add points per price
yz$c<-with(yz,y/z)

yz[which(match(yz$c,max(yz$c))==TRUE),]

我感觉这是一个优化问题

这可能是一个解决方案。可能有更聪明的方法,但是使用优秀的包 RcppAlgos 任务可以以相当快的方式完成

# Setting the framework
numOfElements <- 4
maxPrice <- 28

# Taking all combinations of prices and points
priceCombs <- RcppAlgos::comboGeneral(a$price, numOfElements)
pointCombs <- RcppAlgos::comboGeneral(a$points, numOfElements)

# Computing best choice
magicIndex <- which.max(rowSums(pointCombs[rowSums(priceCombs) <= maxPrice,])) # gives the corresponding index

# results
# points
pointCombs[magicIndex,]
[1] 15 14 13 10 # total of 52
# prices
priceCombs[magicIndex,]
[1] 11.7  6.9  4.5  4.5 # total of 27.6

我已经使用问题中提供的代码生成数据(包含的 28 个是可接受的)。为了可重复性,我在生成数据之前应用了 set.seed(123) - 这样可以观察到相同的数字。


编辑:每个位置两个元素的最佳组合

随着约束的增加(每个 a;b;c 的两个元素)增加了复杂性。我写了一个 dumm 函数,它测试所有(可接受的)可能性。但是,我尝试通过子集

来有效地编写它
bestAllocation <- function (p, maxPrice) {

  # Prelims
  # Create frames per position
  myList <- list(a = p[p$pos == "a",], b = p[p$pos == "b",], c = p[p$pos == "c",])
  # Determining max prices per position
  minPriceA <- min(myList$a$price)
  minPriceB <- min(myList$b$price)
  minPriceC <- min(myList$c$price)
  maxAllowedPriceA <- maxPrice - minPriceB - minPriceC
  maxAllowedPriceB <- maxPrice - minPriceA - minPriceC
  maxAllowedPriceC <- maxPrice - minPriceB - minPriceA
  # Subsetting for efficiency
  myList$a <- myList$a[myList$a$price < maxAllowedPriceA,]
  myList$b <- myList$b[myList$b$price < maxAllowedPriceB,]
  myList$c <- myList$c[myList$c$price < maxAllowedPriceC,]
  # Recode position variables as integers
  myList$a$pos <- 0L
  myList$b$pos <- 1L
  myList$c$pos <- 2L

  # Variables used for the loops
  remainingPrice1 <- remainingPrice2 <- numeric(1)
  indA1 <- indA2 <- indB1 <- indB2 <- indC1 <- indC2 <- logical(nrow(myList$b))
  bestPointsC <- numeric(1)
  resultDF <- data.frame(matrix(0, ncol = 2*3, nrow = 2*3))
  currentMax <- numeric(1)

  # To the loops
  indA1 <- .subset2(myList$a,2L) < maxPrice - minPriceA - 2*minPriceB - 2*minPriceC # keep a's such that we can afford  1a & 2b & 2c 
  indA2 <- .subset2(myList$a,2L) %in% unique(RcppAlgos::comboGeneral(.subset2(myList$a,2L)[indA1], 
                                                                 2L, 
                                                                 constraintFun = "sum", 
                                                                 comparisonFun = "<=", 
                                                                 limitConstraints = maxPrice - 2*minPriceA - 2*minPriceC))

  if (!any(indA1 & indA2)) stop("\nAll combinations of a's exceed the admissible price.") # no admissible tuple of a's

  for (k in 1:nrow(RcppAlgos::comboGeneral(which(indA1 & indA2), 2L)) )
  {
    k1 <- RcppAlgos::comboGeneral(which(indA1 & indA2), 2L)[k,1L]
    k2 <- RcppAlgos::comboGeneral(which(indA1 & indA2), 2L)[k,2L]

    if (sum(.subset2(myList$a,2)[c(k1,k2)]) >= maxPrice - 2*minPriceB - 2*minPriceC) next # not enough money for 2b & 2c

    remainingPrice1 <- maxPrice - sum(.subset2(myList$a,2)[c(k1,k2)])  

    if (all(.subset2(myList$b,2) >= remainingPrice1 - minPriceB - 2*minPriceC )) next # not enough money for 1b & 2c
    indB1 <- .subset2(myList$b,2) < remainingPrice1 - minPriceB - 2*minPriceC 
    indB2 <- .subset2(myList$b,2) %in% unique(RcppAlgos::comboGeneral(.subset2(myList$b,2)[indB1], 
                                                                  2L, 
                                                                  constraintFun = "sum", 
                                                                  comparisonFun = "<=", 
                                                                  limitConstraints = 2*minPriceC))
    if (!any(indB1 & indB2)) next # no admissible tuple of b's

    for (s in 1:nrow(RcppAlgos::comboGeneral(which(indB1 & indB2), 2L))) 
    {
      s1 <- RcppAlgos::comboGeneral(which(indB1 & indB2), 2L)[s,1L]
      s2 <- RcppAlgos::comboGeneral(which(indB1 & indB2), 2L)[s,2L]
      remainingPrice2 <- maxPrice - sum(.subset2(myList$a,2)[c(k1,k2)]) - sum(.subset2(myList$b,2L)[c(s1,s2)])

      if (all(.subset2(myList$c, 2) >= remainingPrice2 - minPriceC)) next # not enough money for 2c

      indC1 <- .subset2(myList$c,2L) < remainingPrice2 - minPriceC
      indC2 <- .subset2(myList$c,2L) %in% unique(RcppAlgos::comboGeneral(.subset2(myList$c,2L)[indC1],
                                                                     2L, 
                                                                     constraintFun = "sum", 
                                                                     comparisonFun = "<=", 
                                                                     limitConstraints = remainingPrice2))
      if (!any(indC1 & indC2)) next # no admissible tuple of c's

      bestPointsC <- sort(.subset2(myList$c,1L)[indC1 & indC2], partial = (sum(indC1 & indC2) - 1L):sum(indC1 & indC2))[(sum(indC1 & indC2) - 1L):sum(indC1 & indC2)]

      if (sum(.subset2(myList$a,1L)[c(k1,k2)]) + sum(.subset2(myList$b,1L)[c(s1,s2)]) + sum(bestPointsC) <= currentMax) next # points value in this iteration lower than the current max 

      currentMax <- sum(.subset2(myList$a,1L)[c(k1,k2)]) + sum(.subset2(myList$b,1L)[c(k1,k2)]) + sum(bestPointsC)
      resultDF <- rbind(myList$a[c(k1,k2),], 
                        myList$b[c(s1,s2),], 
                        myList$c[(.subset2(myList$c,1L) %in% bestPointsC) & indC1 & indC2,]) # maybe add a safety measure (e.g order myList$c[...] by pts, price and keep only first two)
      cat(paste0("\n\nUpdated result",
                 "\nPoints:\t", sum(resultDF$points),
                 "\nPrice :\t", sum(resultDF$price)))  
    }
  }
  return(resultDF)
} 

函数的作用如下

> ans <- bestAllocation(p, maxPrice = 28)


Updated result
Points :    55
Price  :    27.9

Updated result
Points :    58
Price  :    27.9

Updated result
Points :    61
Price  :    27.9

Updated result
Points :    64
Price  :    27.9

Updated result
Points :    67
Price  :    27.8

Updated result
Points :    68
Price  :    27

我相信这可以在这里和那里得到增强,或者有更好的方法来解决它。此外,此函数适用于任何 maxPrice,但仅适用于每个位置选择两个元素的情况。

简短说明

  1. 算出 a;b;c 中最便宜的元素是多少
  2. 考虑两个 a 的所有组合,这样组合价格就足够了,也就是说,一个人至少可以买得起最便宜的 bc 的两倍的
  3. bc
  4. 进行类似的操作
  5. 存储并记住最佳分配