【发布时间】:2021-03-23 16:41:30
【问题描述】:
我正在尝试在 lpsolveAPI 中制定货架优化整数编程算法,并希望添加一个约束,即每个选定货架 (S) 上的每种产品的数量相同:
我的困难是访问和使用总和作为约束(特别是 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)
这在解决之前不出所料是行不通的,一旦解决了就没有效果了。
任何想法如何实现这一目标?提前谢谢你。
【问题讨论】:
-
我怀疑这可能会有所帮助(但我不确定:代码难以阅读):连续变量或整数变量与二进制变量的乘积可以使用标准技术进行线性化。
-
感谢@ErwinKalvelagen 的回复,我为我糟糕的编码表示歉意,这确实使阅读变得困难。但是,“binary_sum”变量确实是一个总和,可以取 0,1 或 2 的值。这使其成为二次约束。我正在重做(尝试)将 MIQLP 用于具有二次约束的线性决策变量。
Gurobi似乎是一种可能的解决方案,尽管不是开源的。任何开源建议将不胜感激。 -
不需要。各个项可以线性化。
标签: r mixed-integer-programming lpsolve