【问题标题】:LPSolve - Setting constraints for the sum of multiple columnsLPSolve - 为多列之和设置约束
【发布时间】:2019-03-16 20:19:11
【问题描述】:

我对使用LPSolve 解决线性优化问题有很好的了解,但有一个方面被难住了。我想为多列的总和创建一个约束。例如,我有一个约束,不允许四个特定列中的任何一个大于 3。但是,我要求四个列中的任何一个都等于 3。

工作示例

在此示例中,我正在制作餐点以优化“价值”,同时保持低于 5 个单独的项目和 40 美元的成本。我还有四种不同的食物组 - 肉类、蔬菜、水果、淀粉 - 我要求任何一组食物中的食物不得超过四项,但任何一组食物必须包含 3 项(这就是我的我被难住了)。

除了最后一个约束之外,下面是得到我想要的结果的代码:

## Choose 5 food items remaining under $40 and maximizing Value ##
## There can be no more than 3 items from the same group chosen, but **there must be 3 items from at least one group**(??) ##

library(dplyr)
library(lpSolve)

# Constraints
totalItems <- 5
totalCost <- 40
maxAllGroups <- 3

# Setup problem
food <- c('Chicken', 'Beef', 'Lamb', 'Fish', 'Pork', 'Carrot', 'Lettuce', 'Asparagus', 'Beats', 'Broccoli', 'Orange', 'Apple', 'Pear', 'Banana', 'Watermelon', 'Potato', 'Corn', 'Beans', 'Bread', 'Pasta')
group <- c('Meat', 'Meat', 'Meat', 'Meat', 'Meat', 'Veggie', 'Veggie', 'Veggie', 'Veggie', 'Veggie', 'Fruit', 'Fruit', 'Fruit', 'Fruit', 'Fruit', 'Starch', 'Starch', 'Starch', 'Starch', 'Starch')
cost <- round(runif(length(food), 1, 20), 0)
value <- round(runif(length(food), 20, 60), 0)
df <- data.frame(food, group, cost, value, stringsAsFactors = FALSE) %>% 
  mutate(Total = 1)

# Value to be maximized
Value <- df$value

# Create constraint vectors
ConVec_Cost <- df$cost
ConVec_Items <- df$Total
# Make `Group` dummy variables
  groups <- unique(df$group)
  ConVec_Groups <- data.frame(row.names = 1:nrow(df))
  for(i in 1:length(groups)){
    currGroup <- groups[i]

    vec <- df %>% 
      mutate(isGroup = (group == currGroup)*1) %>% 
      select(isGroup)
    colnames(vec) <- currGroup

    ConVec_Groups <- cbind(ConVec_Groups, vec)
  }

# ConVec_AnyGroupEqual3 <- ???

ConVec_All <- t(cbind(ConVec_Cost, ConVec_Items, ConVec_Groups))

# Create constraint directions
ConDir_Cost <- "<="
ConDir_Items <- "=="
ConDir_Groups <- rep("<=", ncol(ConVec_Groups))
# ConDir_AnyGroupEqual3 <- "=="
ConDir_All <- c(ConDir_Cost, ConDir_Items, ConDir_Groups)

# Create constraint values
ConVal_Cost <- totalCost
ConVal_Items <- totalItems
ConVal_Groups <- rep(maxAllGroups, ncol(ConVec_Groups))
# ConVal_AnyGroupEqual3 <- 1 #1 group should have 3
ConVal_All <- c(ConVal_Cost, ConVal_Items, ConVal_Groups)

# Solve
sol <- lpSolve::lp("max",
                   objective.in = Value,
                   const.mat    = ConVec_All,
                   const.dir    = ConDir_All,
                   const.rhs    = ConVal_All,
                   all.bin      = TRUE
)

# Solution
df[sol$solution == 1,]

如果我需要一个特定的食物组来拥有 3 个,那么这很容易,但我需要任何一个组为 3 个的事实却让这变得困难。有没有办法在不诉诸LPSolveAPI(我承认对此知之甚少)的情况下做到这一点?

【问题讨论】:

    标签: r linear-programming lpsolve


    【解决方案1】:

    不久前,我通过剖析某人对类似问题的回答来弄清楚这一点。基本上,您必须添加 5 个“虚拟行”(每个食物组一个),对角线值为 -3。然后,您必须添加另一列,除了最后 5 行为 1(您刚刚添加到矩阵中的行)之外,所有值都为 0。您强制新列至少为 1,这意味着必须选择最后 5 行之一。

    由于将选择其中一个行,并且该行将 -3 添加到它所在的任何食物组,并且您有强制每个食物组至少为 0 的约束,然后选择的 -3 行将强制该食物组列选择 3,以便总和 >= 0。

    新代码

    ## Choose 5 food items remaining under $40 and maximizing Value ##
    ## There can be no more than 3 items from the same group chosen, but **there must be 3 items from at least one group**(??) ##
    
    library(dplyr)
    library(lpSolve)
    
    # Constraints
    totalItems <- 5
    totalCost <- 40
    minAllGroups <- 0
    atLeastNFrom1 <- 3
    
    # Setup problem
    food <- c('Chicken', 'Beef', 'Lamb', 'Fish', 'Pork', 'Carrot', 'Lettuce', 'Asparagus', 'Beats', 'Broccoli', 'Orange', 'Apple', 'Pear', 'Banana', 'Watermelon', 'Potato', 'Corn', 'Beans', 'Bread', 'Pasta')
    group <- c('Meat', 'Meat', 'Meat', 'Meat', 'Meat', 'Veggie', 'Veggie', 'Veggie', 'Veggie', 'Veggie', 'Fruit', 'Fruit', 'Fruit', 'Fruit', 'Fruit', 'Starch', 'Starch', 'Starch', 'Starch', 'Starch')
    cost <- round(runif(length(food), 1, 20), 0)
    value <- round(runif(length(food), 20, 60), 0)
    df <- data.frame(food, group, cost, value, stringsAsFactors = FALSE) %>% 
      mutate(Total = 1)
    
    
    # Create constraint vectors
    ConVec_Cost <- df$cost
    ConVec_Items <- df$Total
    # Make `Group` dummy variables
      groups <- unique(df$group)
      ConVec_Groups <- data.frame(row.names = 1:nrow(df))
      for(i in 1:length(groups)){
        currGroup <- groups[i]
    
        vec <- df %>% 
          mutate(isGroup = (group == currGroup)*1) %>% 
          select(isGroup)
        colnames(vec) <- currGroup
    
        ConVec_Groups <- cbind(ConVec_Groups, vec)
      }
    
    # New vector for atleast
    ConVec_AtLeastN <- 0
    
    ConVec_All <- cbind(ConVec_Cost, ConVec_Items, ConVec_Groups, ConVec_AtLeastN)
    
    # Add the negative values to the matrix
    ConVec_All <- rbind(ConVec_All, c(0,0,-atLeastNFrom1,0,0,0,1))
    ConVec_All <- rbind(ConVec_All, c(0,0,0,-atLeastNFrom1,0,0,1))
    ConVec_All <- rbind(ConVec_All, c(0,0,0,0,-atLeastNFrom1,0,1))
    ConVec_All <- rbind(ConVec_All, c(0,0,0,0,0,-atLeastNFrom1,1))
    
    # Create constraint directions
    ConDir_Cost <- "<="
    ConDir_Items <- "=="
    ConDir_Groups <- rep(">=", ncol(ConVec_Groups))
    ConDir_AtLeastN <- ">="
    ConDir_All <- c(ConDir_Cost, ConDir_Items, ConDir_Groups, ConDir_AtLeastN)
    
    # Create constraint values
    ConVal_Cost <- totalCost
    ConVal_Items <- totalItems
    ConVal_Groups <- rep(minAllGroups, ncol(ConVec_Groups))
    ConVal_AtLeastN <- 1
    ConVal_All <- c(ConVal_Cost, ConVal_Items, ConVal_Groups, ConVal_AtLeastN)
    
    
    # Value to be maximized
    Value <- c(df$value, rep(0, nrow(ConVec_All) - length(df$value)))
    
    # Solve
    sol <- lpSolve::lp("max",
                       objective.in = Value,
                       const.mat    = t(ConVec_All),
                       const.dir    = ConDir_All,
                       const.rhs    = ConVal_All,
                       all.bin      = TRUE
    )
    
    # Solution
    df[sol$solution[1:nrow(df)] == 1,]
    

    【讨论】:

    • 我运行了这个例子,看起来没有遵循最高价格限制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多