【问题标题】:Use sum of binary values as constraint coefficient in integer programming constraint在整数规划约束中使用二进制值的总和作为约束系数
【发布时间】:2021-03-23 16:41:30
【问题描述】:

我正在尝试在 lpsolveAPI 中制定货架优化整数编程算法,并希望添加一个约束,即每个选定货架 (S) 上的每种产品的数量相同:

f

我的困难是访问和使用总和作为约束(特别是 Xij

我可以重新表述以使其成为线性而没有太多问题(请原谅伪代码):

sum(X_ij)*F_ij - sum(F_ij) = 0

此操作可能会影响 X 本身的选择,因此我需要它是动态的(否则我可以在求解后更改值)我如何访问这些值,或将 F 值编码为相等?

线性程序通过一系列约束来创建一个二元解,将产品放置在货架上(目前可能放置在一个货架或两个货架上),有四个货架。然后是第二组,它允许那些货架上的产品数量不为零,受产品宽度相对于货架长度的限制,所有货架上的产品最大数量是给定的(其中大多数是 8 个,虽然这有点武断),最大化产品利润。所有这些都按预期工作。但是,我希望添加一个约束,以使两个或多个货架上的产品数量相同,即一个上四个,另一个上四个。鉴于使用的货架数量可以是 1 或 2,我不能简单地划分值。此外,由于哪些货架被占用是由约束决定的,我不能简单地使用 P1S1 = P1S2 (除非我可以选择占用的货架,我没有这样做)

这是我正在尝试做的一个最小示例(请原谅我第一次这样做时不优雅的代码)数据集是here

library(lpSolveAPI)

shelves <- data.frame(Sl_i = c(151, 200, 180, 218),
                      Sh_i = c(30, 30, 30, 36))

datatable <- read.csv("~/Desktop/sales/datatable.txt", sep="")

S = 4  # number of shelves
P = 40 # number of products

Shelf_choice <-
  make.lp(0, nrow(mydata) * 2) # create the lp object with decision variables == longitude of data.frame

#### SET OBJECTIVE FUNCTION ####


#### Set controls for the model ####

lp.control(Shelf_choice,
           sense = "max",
           timeout = 10,
           presolve = "none") ## timeout prevents getting stuck
set.objfn(Shelf_choice, c(rep(rep(0, nrow(
  mydata
)), 1), mydata$Pu_j)) # maximize profit (Pu_j)

set.type(Shelf_choice, 1:nrow(mydata), "binary") # present on shelf or not
set.type(Shelf_choice, 1:nrow(mydata) + nrow(mydata), "integer") # number of product j on shelf


### Assure that each product appears on minimum number of shelves (1 in this case)
Add_productShelf_constraint <- function (prod_index) {
  cargo_cols <-
    (0:(S - 1)) * P + prod_index  # # index of products by column (eg. 1,41,81,121)
  add.constraint(
    Shelf_choice,
    rep(1, S),
    # repeat value the same number of times as shelves
    indices = cargo_cols,
    type = ">=",
    rhs = mydata$smin_j[prod_index]
  ) # value of minimum number of shelves
}

lapply(1:P, Add_productShelf_constraint) # list apply this for every product

### Assure that product appears no more than the number of shelves permitted (2 in this case)
Add_productShelfMAX_constraint <- function (prod_index) {
  cargo_cols <-
    (0:(S - 1)) * P + prod_index   # index of products by column (eg. 1,41,81,121)
  add.constraint(
    Shelf_choice,
    rep(1, S),
    # repeat value the same number of times as shelves
    indices = cargo_cols,
    type = "<=",
    rhs = mydata$smax_j[prod_index]
  ) # value of minimum number of shelves
}

lapply(1:P, Add_productShelfMAX_constraint) # list apply this for every product



### Third Constraint: Products too tall for a shelf are excluded

Add_height_constraint <-
  function (prod_index) {
    # this needs to be improved
    add.constraint(Shelf_choice,
                   1,
                   indices = prod_index,
                   type = "=",
                   rhs = 0)
  }
lapply(which(mydata$height == 0), Add_height_constraint) # Here we select the colums which have 0 (don't fit), and set the value to 0


## Products are on consecutive shelves - this currently only works for two shelves
Add_nextshelf_constraint <- function (prod_index) {
  mat1 <- combn(1:S, 2)[, which(combn(1:S, 2)[2, ] - combn(1:S, 2)[1, ] != 1)]
  cargo_cols <- (0:(S - 1)) * P + prod_index
  result <- matrix(cargo_cols[mat1], nrow = 2)
  
  for (i in 1:ncol(result)) {
    add.constraint(
      Shelf_choice,
      c(1, 1),
      indices = result[, i],
      type = "<=",
      rhs = 1
    )
  }
}
lapply(1:P, Add_nextshelf_constraint)




### Product facings only appear on selected shelves (where Xij = 1)

Add_FF_constraint1 <- function (prod_index) {
  Y01col <- prod_index
  print(Y01col)
  FF_col <- prod_index + nrow(mydata)
  add.constraint(
    Shelf_choice,
    c(1, -100),
    indices = c(FF_col, Y01col),
    type = "<=",
    rhs = 0
  )
}
lapply(1:nrow(mydata), Add_FF_constraint1) #

Add_FF_constraint2 <- function (prod_index) {
  Y01col <- prod_index
  FF_col <- prod_index + nrow(mydata)
  add.constraint(
    Shelf_choice,
    c(1, -1),
    indices = c(FF_col, Y01col),
    type = ">=",
    rhs = 0
  )
}
lapply(1:nrow(mydata), Add_FF_constraint2) #



#### Sum of product widths on shelves does not exceed shelf length
Add_FijShelflength_constraint <- function (shelf_index) {
  shelf_cols_mydata <- ((1:(P)) + (shelf_index - 1) * P)
  FF_shelf_cols <- ((1:(P)) + (shelf_index - 1) * P)  + nrow(mydata)
  
  add.constraint(
    Shelf_choice,
    c(mydata$Pw_j[shelf_cols_mydata]),
    # width of each product
    indices = c(FF_shelf_cols),
    # indices of products per shelf in Fij Matrix
    rhs = shelves$Sl_i[shelf_index]
  ) # length of each shelf
  
}
lapply(1:S, Add_FijShelflength_constraint) # list apply this by shelf index

## add minimum number of total facings
Add_min_facings_constraint <- function (prod_index) {
  FjSi_cols <-
    (0:(S - 1)) * P + prod_index + nrow(mydata) # index of the products by column in out table
  add.constraint(
    Shelf_choice,
    rep(1, S),
    # repeat value the same number of times as shelves
    indices = FjSi_cols,
    # index of products by column (eg. 1,41,81,121)
    type = ">=",
    rhs = mydata$Fmin_j[prod_index]
  ) # value of minimum number of products
}
lapply(1:P, Add_min_facings_constraint) # list apply this for every product

## add maximum number of facings
Add_max_facings_constraint <- function (prod_index) {
  FjSi_cols <-
    (0:(S - 1)) * P + prod_index + nrow(mydata)
  add.constraint(
    Shelf_choice,
    rep(1, S),
    # repeat value the same number of times as shelves
    indices = FjSi_cols,
    # index of products by column (eg. 1,41,81,121)
    type = "<=",
    rhs = mydata$Fmax_j[prod_index]
  ) # value of maximum number of products
}
lapply(1:P, Add_max_facings_constraint) # list apply this for every product

solve(Shelf_choice)

get.objective(Shelf_choice) # gives the total value of the facings

### Tabulates the results ####
test <- matrix(get.variables(Shelf_choice),
               ncol = S * 2,
               byrow = F)
rownames(test) <- paste0("Product", 1:40)
colnames(test) <- c(rep(paste0("Shelf", 1:4), 2))

test[, 5:8] # shows the product placements (uneven products between shelves)
#

结果:

Product Shelf 1 Shelf 2 Shelf 3 Shelf 4
P1 0 0 0 2
- - - - -
P11 1 7 0 0
P16 2 2 0 0

例如,我需要产品 11 在每个货架上的产品数量相同(每个货架上 4 个)

我试图创建一个约束,例如:

Sum_Xshelf_constraint <- function (prod_index) {
  binary_sum <-
    sum(get.variables(Shelf_choice)[(0:(S - 1)) * P + prod_index])
  total_Fij <-
    sum(get.variables(Shelf_choice)[(0:(S - 1)) * P + prod_index + nrow(df)])
  
  total_cols <-
    (0:(S - 1)) * P + prod_index + nrow(df) # index of the products in Fij
  
  for (i in 1:length(total_cols)) {
    add.constraint(
      Shelf_choice,
      c(binary_sum),
      indices = c(total_cols[i]),
      type = "<=",
      rhs = total_Fij
    )                  # value of minimum number which is Fmin_j
  }
}
### At least one product on a shelf
lapply(1:P, Sum_Xshelf_constraint) 

这在解决之前不出所料是行不通的,一旦解决了就没有效果了。

任何想法如何实现这一目标?提前谢谢你。

【问题讨论】:

  • 我怀疑这可能会有所帮助(但我不确定:代码难以阅读):连续变量或整数变量与二进制变量的乘积可以使用标准技术进行线性化。
  • 这是一个看起来相似的例子:yetanothermathprogrammingconsultant.blogspot.com/2017/05/…
  • 感谢@ErwinKalvelagen 的回复,我为我糟糕的编码表示歉意,这确实使阅读变得困难。但是,“binary_sum”变量确实是一个总和,可以取 0,1 或 2 的值。这使其成为二次约束。我正在重做(尝试)将 MIQLP 用于具有二次约束的线性决策变量。 Gurobi 似乎是一种可能的解决方案,尽管不是开源的。任何开源建议将不胜感激。
  • 不需要。各个项可以线性化。

标签: r mixed-integer-programming lpsolve


【解决方案1】:

如果您的决策变量是 PiSj(第 j 个货架上的#ith 产品),那么一系列链式方程可以工作:

P1S1 = P1S2
P1S2 = P1S3
P1S3 = P1S4

这种简单性有时可以显着提高性能。

【讨论】:

  • 我希望它是那么简单。问题是产品 1 可能在货架 1 和货架 2 上,但可能不在货架 3 或 4 上,或者甚至可能只在一个货架上。因此,需要“知道”它在多少架子上,并将一个架子的值乘以该数,然后除以所有架子上的总数,以确保占用的架子上的数量相同。
【解决方案2】:

我必须在这里回答我自己的问题:

这是不可能的。

仔细阅读,似乎这个特殊的约束是二次的...(http://lpsolve.sourceforge.net/5.5/

"假设xj必须取整数值i:

yj / y0 = i

yj = i * y0

不幸的是,lpsolve 无法处理此约束,因为它是二次的。”

完成后,我将使用二次解法编辑此答案。我想我最好现在发布这个,这样其他人就不会花费太多精力来尝试解决它。​​

谢谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多