Как настроить оптимизацию линейного программирования в R с помощью LpSolve?

Например, у меня есть эти образцы данных:

d=data.frame(x=c(1,1,1,2,2,3,4,4),y=c(5,6,7,8,7,5,6,5),w=c(1,2,3,4,5,6,7,8))

Это выглядит так:

  x y w
1 1 5 1
2 1 6 2
3 1 7 3
4 2 8 4
5 2 7 5
6 3 5 6
7 4 6 7
8 4 5 8

x и y представляют индексы из datax и datay. w представляет собой результат сравнения datax[x] с datay[y]. Я хочу максимизировать общий балл (или w) из d, где каждому значению x соответствует не более одного значения y, и наоборот.

Результат должен выглядеть так:

  x y w
1 2 7 5
2 3 5 6
3 4 6 7

Где сумма всех w значений максимизирована, и каждое x и каждое y отображаются в результате только один раз.

Как мне решить эту проблему в функции lpSolve::lp?


person eyio    schedule 16.11.2015    source источник
comment
Я предполагаю, что ваш ожидаемый результат неверен, поскольку 5 появляется два раза в y. Или я неправильно понял, что вы хотите?   -  person etienne    schedule 16.11.2015
comment
@etienne да прости за это! Я там ошибся, обновил (на этот раз должно быть исправлено). Не все значения x или y нужно включать, нужно просто увеличить вес.   -  person eyio    schedule 16.11.2015


Ответы (1)


Вы можете использовать lpSolveAPI для решения вашей проблемы. Заявленное вами решение не совсем осуществимо с учетом ваших ограничений. Итак, давайте продолжим с вашим желанием, чтобы X и Y не повторялись в решении.

Вам понадобится 8 новых двоичных переменных. Каждая переменная определяет, выбирается ли эта строка в d (1) или отбрасывается (0).

Обновление по запросу OP

Да, код lpSolveAPI (ниже) делает его более сложным, чем есть на самом деле. Эта формулировка LP (вывод lpSolveAPI) должна прояснить ситуацию:

/* Objective function */
max: +pick_1 +2 pick_2 +3 pick_3 +4 pick_4 +5 pick_5 +6 pick_6 +7 pick_7 +8 pick_8;

/* Constraints */
OneX_1: +pick_1 +pick_2 +pick_3 <= 1;
OneX_2: +pick_4 +pick_5 <= 1;
OneX_4: +pick_7 +pick_8 <= 1;
OneY_5: +pick_1 +pick_6 +pick_8 <= 1;
OneY_6: +pick_2 +pick_7 <= 1;
OneY_7: +pick_3 +pick_5 <= 1;

/* Variable bounds */
pick_1 <= 1;
pick_2 <= 1;
pick_3 <= 1;
pick_4 <= 1;
pick_5 <= 1;
pick_6 <= 1;
pick_7 <= 1;
pick_8 <= 1;

Объяснение: Второе ограничение (OneX_2) просто утверждает, что только один из pick_4 или pick_5 может быть 1, поскольку 4-я и 5-я строки в фрейме данных d имеют X = 2

Решение

Обратите внимание, что приведенная выше формулировка дает оптимальное решение, которое выбирает 4 строки во фрейме данных d

> d[c(3,4,6,7),]
  x y w
3 1 7 3
4 2 8 4
6 3 5 6
7 4 6 7

Сумма w равна 20, что лучше, чем решение в вопросе.

Код

library(lpSolveAPI)
d <- data.frame(x=c(1,1,1,2,2,3,4,4),y=c(5,6,7,8,7,5,6,5),w=c(1,2,3,4,5,6,7,8))

ncol <- 8 #you have eight rows that can be picked or dropped from the solution set
lp_rowpicker <- make.lp(ncol=ncol)
set.type(lp_rowpicker, columns=1:ncol, type = c("binary"))

obj_vals <- d[, "w"]
set.objfn(lp_rowpicker, obj_vals) 
lp.control(lp_rowpicker,sense='max')

#Add constraints to limit X values from repeating
add.constraint(lp_rowpicker, xt=c(1,1,1), #xt specifies which rows of the LP
               indices=c(1,2,3), rhs=1, type="<=")
add.constraint(lp_rowpicker, xt=c(1,1), #xt specifies which rows of the LP
               indices=c(4,5), rhs=1, type="<=")
add.constraint(lp_rowpicker, xt=c(1,1), #xt specifies which rows of the LP
               indices=c(7,8), rhs=1, type="<=") #x's in dataframe rows 7 & 8 are both '4'

#Add constraints to limit Y values from repeating
add.constraint(lp_rowpicker, xt=c(1,1,1), #xt specifies which rows of the LP
               indices=c(1,6,8), rhs=1, type="<=") #Y's in df rows 1,6 & 8 are all '5'
add.constraint(lp_rowpicker, xt=c(1,1), #xt specifies which rows of the LP
               indices=c(2,7), rhs=1, type="<=") #Y's in dataframe rows 2&7 are both '6'
add.constraint(lp_rowpicker, xt=c(1,1), #xt specifies which rows of the LP
               indices=c(3,5), rhs=1, type="<=") #y's in dataframe rows 3&5 are both '7'

solve(lp_rowpicker)
get.objective(lp_rowpicker) #20
get.variables(lp_rowpicker)
#[1] 0 0 1 1 0 1 1 0
#This tells you that from d you pick rows: 3,4,6 & 7 in your optimal solution.

#If you want to look at the full formulation:
rownames1 <- paste("OneX", c(1,2,4), sep="_")
rownames2 <- paste("OneY", c(5,6,7), sep="_")
colnames<- paste("pick_",c(1:8), sep="")
dimnames(lp_rowpicker) <- list(c(rownames1, rownames2), colnames)
print(lp_rowpicker)

#write it to a text file
write.lp(lp_rowpicker,filename="max_w.lp")

Надеюсь, это даст вам представление о том, как использовать lpSolveAPI для формулирования вашей проблемы.

person Ram Narasimhan    schedule 16.11.2015
comment
Спасибо за твой ответ! Я ошибся с желаемым результатом. Я обновил его сейчас, и это должно иметь смысл с учетом ограничений. Не все значения x или y нужно включать, нужно просто увеличить вес. Я изучу ответ более подробно, но он кажется довольно сложным, можно ли решить проблему просто функцией lp с обновленным желаемым результатом? - person eyio; 16.11.2015
comment
Я обновил ответ, добавив фактическую математическую формулировку и объяснив ограничения. Это должно прояснить ситуацию. Обратите внимание, что решающая программа смогла найти лучшее решение, чем то, которое вы дали. - person Ram Narasimhan; 17.11.2015