R中带条件约束的线性规划

时间:2016-12-01 01:01:16

标签: r linear-programming knapsack-problem

我有线性编程问题,我试图从大量二进制资源中进行选择以优化价值,基本上是背包问题。我遇到的问题是不同的资源具有共同的特征,我想确保我的最终解决方案具有0或2个具有特定特征的资源。有没有办法实现这个目标?尽管进行了广泛的搜索,我仍然无法想到一个或找到一个。在我的数据中,决策变量是资源,约束是这些资源的特征。请考虑以下代码:

library(lpSolve)
const_mat_so<-matrix(c(
  c(0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,1,0,0,1,0,1)
     ,c(0,  0,  0,  0,  0,  0,  0,  1,  0,  1,  0,1,1,0,0,1,1)
     ,c(0,  0,  0,  0,  0,  1,  1,  0,  0,  0,  1,0,1,0,1,0,0)
     ,c(1,  1,  0,  1,  1,  0,  0,  0,  1,  0,  0,0,0,0,0,0,0)
     ,c(8800,   8500,   7600,   8600,   8400,   7500, 7000, 8500,   8800,   7700,   6700,5500,1200,6700,9500,8700,6500)
     ,c(0,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,0,0,0,0,0,0)
     ,c(0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,0,0,0,0,0,0)
     ,c(0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,0,0,0,0,0,0)
     ,c(0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  1,0,0,1,0,1,0)
     ,c(0,  0,  1,  0,  0,  0,  0,  0,  1,  0,  0,1,1,0,0,0,0)
     ,c(0,  0,  1,  0,  0,  0,  0,  1,  0,  0,  0,0,0,0,0,0,0)
     ,c(0,  0,  0,  1,  0,  0,  0,  0,  0,  1,  0,1,1,1,0,1,0)
     ,c(0,  1,  0,  0,  0,  0,  0,  1,  0,  0,  0,0,0,0,0,1,0)
     ,c(0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,0,0,0,1,0,0)
     ,c(0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  1,0,0,0,0,0,0)
     ),nrow=15,byrow = TRUE)

const_dir_so<-c("=","=","=","=","<=","<=","<=","<=","<=","<=","<=","<=","<=","<=","<=")

max_cost_so = 25000

objective_so = c(21.0, 19.3, 19.2, 18.8, 18.5, 16.6, 16.4, 16.4, 16.0, 16.0, 14.9, 14.6, 14.0, 13.9,12.0,5.5,24.6)

const_rhs_so<-c(1,1,1,1,25000,3,3,3,2,2,2,2,2,2,2)

x = lp ("max", objective_so, const_mat_so, const_dir_so, const_rhs_so,     all.bin=TRUE, all.int=TRUE 
    )

> x
Success: the objective function is 68.1

> x$solution
 [1] 1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0

虽然上面提出了一个解决方案,但它不是我想要的解决方案,因为我实际上希望最后七个约束是&gt; = 2或0.我不知道如何编码这个或它是否可能。任何帮助,将不胜感激。我不是一个线性编程高手,所以请原谅任何关于这种方法的误解。

3 个答案:

答案 0 :(得分:2)

我的理解是最后7个约束中的每一个都要大于2或等于零,即不是1.

1)只有7个这样的约束,所以有2 ^ 7 = 128种可能性足够小,我们可以使用给定的配方运行每一个,没有过多的运行时间然后找到最大值。

dec2bin取一个基数10(即十进制)数,并将其转换为0和1的二进制向量。在0到127之间的每个数字上运行它给出二进制数,使得1对应于> = 2的约束(其余等于0)。

dec2bin <- function(dec, digits = 7) {
   # see http://stackoverflow.com/questions/6614283/converting-decimal-to-binary-in-r
   tail(rev(as.integer(intToBits(dec))), digits)
}

runLP <- function(i) {
   bin <- dec2bin(i)
   n <- length(const_rhs_so)  # 15
   ix <- seq(to = n, length = length(bin))  # indexes of last 7 constraints, i.e. 9:15
   const_dir_so[ix] <- ifelse(bin, ">=", "=")
   const_rhs_so[ix] <- 2*bin
   lp("max", objective_so, const_mat_so, const_dir_so, const_rhs_so, all.bin = TRUE)
}

lpout <- lapply(0:127, runLP)
ixmax <- which.max(sapply(lpout, "[[", "objval"))
ans <- lpout[[ixmax]]
ans
ans$solution
tail(c(const_mat_so %*% ans$solution), 7)

,并提供:

> ans
Success: the objective function is 62 
> ans$solution
 [1] 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1
> tail(c(const_mat_so %*% ans$solution), 7) # last 7 constraint values
[1] 0 0 0 0 0 0 0

2)在@Erwin Kalvelagen的第二个替代方案中,它指的是约束变量,但我认为其意思是x在他的答案中是LHS的价值最后7个约束之一。也就是说,如果C是原始最后7个约束的矩阵,那么用这14个约束替换原来的7个约束:

 Cx + D1 y <= 0
 Cx + D2 y >= 0

其中D1是对角线矩阵,其对角线元素是任何足够大的负数,D2是对角线矩阵,其对角线元素都是-2。这里我们优化二进制变量的xy向量。 x变量与问题中的一样,并且有7个新的y二元变量,因此y [i]为0,将最后7个原始约束的第i个约束为0或1,以将其约束为2个或更多。 {1}}变量在(1)中称为y。目标中bin变量的系数均为零。

就lpSolve R代码而言:

y

给出与(1)中相同的62值。 objective_so2 <- c(objective_so, numeric(7)) const_mat_so2 <- cbind(rbind(const_mat_so, const_mat_so[9:15, ]), rbind(matrix(0, 8, 7), diag(-100, 7), diag(-2, 7))) const_dir_so2 <- c(const_dir_so, rep(">=", 7)) const_rhs_so2 <- c(const_rhs_so[1:8], numeric(14)) x2 = lp ("max", objective_so2, const_mat_so2, const_dir_so2, const_rhs_so2, all.bin = TRUE) 变量(最后7个)都是0,也对应于(1)。这也提供了一个双重检查,因为现在有两种方法给出了一致的答案。

y

答案 1 :(得分:2)

我相信LpSolve支持半连续变量。具有下界L和上界U的半连续变量可以采用值0或L和U之间。我不确定R包lpSolve是否支持此变量类型。

但是我们可以使用额外的二进制变量<div align="center"><div style="width:600px;"> 和额外的约束来模拟这个。因此,您需要使y变量连续(如果只需要整数值,则为整数)并添加约束:

x

其中2*y <= x <= U*y U的上限。

答案 2 :(得分:2)

lpSolveAPI包为“lp_solve”提供了更高级的接口。正如@Erwin Kalvelagen所提到的,“lp_solve”和lpSolveAPI支持半连续变量(半连续决策变量可以在其上限和下限之间采用允许值以及零)。约束矩阵使您能够将第9-15个约束公式的输出转换为18-24个变量。例如(约第9个约束),x6 + x11 + x14 + x16 - x18 = 0x6 + x11 + x14 + x16 = x18时。所以我认为你可以通过半连续变量x6 + x11 + x14 + x16来控制x18

library(lpSolveAPI)

  ## add 18-24th cols to define the 18-24th variables
const_mat_so2 <- cbind(const_mat_so, rbind(matrix(0, nrow = 8, ncol = 7), diag(-1, 7)))

  ## [EDITED] make a model and set a constraint matrix and objective coefs
model <- make.lp(nrow(const_mat_so2), 0)

for(i in 1:ncol(const_mat_so2)) add.column(model, const_mat_so2[,i])
set.constr.type(model, c(const_dir_so[-c(9:15)], rep("=", 7)))
set.rhs(model, c(const_rhs_so[-c(9:15)], rep(0, 7)))   # each original output - 18-24th = 0

set.objfn(model, c(objective_so, rep(0, 7)))           # 18-24th are 0

  ## define semi-continuous and bounds.
set.semicont(model, col = 18:24)
set.bounds(model, lower = rep(1.9, 7), col = 18:24)   # default upper is Inf.

  ## define other things
set.type(model, col = 1:17, type = "binary")     # original variable
set.type(model, col = 18:24, type = "integer")   # outputs of original constraint formulas
lp.control(model, sense = "max")                 # do maximize

# write.lp(model, "filename.lp", "lp")   # if you want to watch the whole model
 
solve(model)
get.variables(model)
 # [1] 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 [18] 0 0 0 0 0 0 0
get.objective(model)
 # [1] 62
t(const_mat_so %*% res[1:17])
 #      [,1] [,2] [,3] [,4]  [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15]
 # [1,]    1    1    1    1 22300    1    0    0    0     0     0     0     0     0     0