#r #optimization #constraints #knapsack-problem #lpsolve
#r #оптимизация #ограничения #рюкзак-проблема #lpSolve
Вопрос:
Проблема
Я использую lpSolve для поиска оптимального состава для фэнтезийной бейсбольной команды — задача о рюкзаке, включающая цену SALARY
и прогнозируемые очки DK
каждого игрока PLAYERID
в рамках заданных ограничений соревнования.
Текущий код работает отлично, но у меня есть ограничение, которое я хотел бы добавить, которое я не совсем понимаю. Новое ограничение заключается в том, чтобы в составе не было игроков, играющих с одним из питчеров SP
в том же составе.
Что у меня есть до сих пор
Я создал столбец с именем MNBT
(не должен быть вместе), который определяет питчера соперника PLAYERID
, которого нельзя найти в том же составе, что и у каждого игрока, но я застрял там. Первые 20 строк data.frame slate_players
следующие (при необходимости я могу предоставить все 91 строку для этого конкретного конкурса):
PLAYERID POS TEAM OPP SALARY DK TEAM_O MNBT
1 37584 SP LAD OAK 10000 18.42 0SP 13170
2 11292 SP TEX HOU 9300 18.41 0SP 1452665
3 1452665 SP HOU TEX 7400 15.22 0SP 11292
4 11168 SP BAL BOS 6900 9.06 0SP 13502
5 13170 SP OAK LAD 6800 6.06 0SP 37584
6 13502 SP BOS BAL 6700 13.52 0SP 11168
7 2038873 SP KCR DET 6600 18.45 0SP 34649
8 34649 SP DET KCR 6500 7.46 0SP 2038873
9 11446 C KCR DET 5300 7.55 KCR 34649
10 1054004 C LAD OAK 5000 8.25 LAD 13170
11 15541 C BOS BAL 4500 7.08 BOS 11168
12 1252110 C OAK LAD 4100 5.07 OAK 37584
13 22667 C BAL BOS 3400 7.09 BAL 13502
14 10290 C TEX HOU 2900 4.08 TEX 1452665
15 13171 C DET KCR 2800 5.45 DET 2038873
16 17552 C HOU TEX 2600 4.47 HOU 11292
17 36727 1B LAD OAK 5800 9.09 LAD 13170
18 17648 1B LAD OAK 5400 8.57 LAD 13170
19 17887 1B OAK LAD 4900 7.30 OAK 37584
20 17851 1B KCR DET 4400 7.24 KCR 34649
[...]
Текущий код lpSolve
# count the unique players and teams on the slate
unique_teams = unique(slate_players$TEAM_O)
unique_players = unique(slate_players$PLAYERID)
# define the objective for the solver
obj = slate_players$DK
# create a constraint matrix for the solver
con = rbind(t(model.matrix(~ POS 0, slate_players)), #Positions
t(model.matrix(~ PLAYERID 0, slate_players)), #DupPlayers
t(model.matrix(~ TEAM_O 0, slate_players)), #SameTeam
rep(1,nrow(slate_players)), #TotPlayers
slate_players$SALARY) #MaxSalary
# set the direction for each of the constraints
dir = c("==", #1B
"==", #2B
"==", #3B
"==", #C
"==", #OF
"==", #SP
"==", #SS
rep('<=',length(unique_players)), #DupPlayers
rep('<=',length(unique_teams)), #SameTeam
"==", #TotPlayers
"<=") #MaxSalary
# set the limits for the right-hand side of the constraints
rhs = c(1, #1B
1, #2B
1, #3B
1, #C
3, #OF
2, #SP
1, #SS
rep(1,length(unique_players)), #DupPlayers
rep(5,length(unique_teams)), #SameTeam
10, #TotPlayers
50000) #MaxSalary
# find the optimal solution using the solver
result = lp("max", obj, con, dir, rhs, all.bin = TRUE)
# create a table for the players that are in optimal solution
solindex = which(result$solution==1)
optsolution = slate_players[solindex,]
Вопрос
Как мне закодировать это новое ограничение? Я выполнял подобные настройки вручную, но я был бы очень признателен, если бы было доступно решение для автоматизации этого процесса. Спасибо!
Комментарии:
1. Я не уверен, как бы вы закодировали это в R, но типичный способ сделать ограничение типа «или-или» — это добавить переменные выбора вместе как:
x x' <= 1 for all (x, x') in set of prohibited combos
2. @AirSquid Да, у меня есть такая логика, чтобы гарантировать, что в составе нет дублирующих игроков (некоторые игроки имеют право на несколько позиций), но я не совсем понял, как перевести это в то, что мне нужно здесь. Я уверен, что это правильная линия мышления — я просто еще не совсем до решения.
3. @Eric_Alan выполняется ли приведенный выше сценарий для вас? Я получаю следующую ошибку. Я думаю, это может быть связано с тем, что вы сократили список игроков. Мне также пришлось изменить некоторые ограничения, прежде чем пытаться использовать решатель, поскольку в этом фрейме данных всего три типа игроков. Предупреждающее сообщение: в rbind(const.mat, const.dir.num, const.rhs): количество столбцов результата не кратно длине вектора (аргумент 2)
4. Когда я удалил ограничение дублирования проигрывателя, я смог запустить код. Смотрите мою предложенную идею ниже. Поскольку all.bin = TRUE , ограничение дублирующего игрока в любом случае было избыточным.
5. @windyvation Вы правы — приведенный выше скрипт не запускается должным образом без всех 91
slate_players
строк фрейма данных, который включает все семь типов игроков. Ограничение дублирования игроков вступает в действие во всем наборе данных, поскольку есть несколько игроков, которые имеют право играть на нескольких позициях, но могут появиться в оптимальном решении только один раз.
Ответ №1:
В итоге вместо одного MNBT
столбца я создал вспомогательный столбец для каждого отдельного питчера SP
, чтобы указать, с какими нападающими он может не появиться в оптимальном решении. В этих столбцах я присвоил значение для питчера 5
и значение 1
для каждого нападающего, с которым он не должен появляться. Затем ограничение стало таким, что сумма каждого из этих столбцов будет равна <=
5
. Логика заключается в том, что максимум 5
нападающих могут быть в одном составе перед любым отдельным питчером, но если бы этот же питчер появился в оптимальном решении, ни один из нападающих, с которыми он столкнулся, не был бы.
Ответ №2:
Хотя это не создает для вас набор ограничений, этот пример является одним из тех ограничений, которые, как упоминал @AirSquid, могут помочь.
В приведенном выше примере 6-й игрок (13502) не смог сыграть против 13-го игрока (22667).
Добавить к ограничению:
c(0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0)
Добавить в инструкции:
"<="
Добавить в правую часть:
1
Следующий трюк заключается в том, как сгенерировать все эти наборы ограничений в R. Cheerio.
Комментарии:
1. ДА. Это то решение, которое я искал (и использовал вариант ограничения дублирующего игрока), но не совсем понял, как применить на практике для игроков «не должны быть вместе».
2. @Eric_Alan как вы в конечном итоге кодировали матрицу ограничений для ограничения отбивающих и питчеров на основе ответа windyvation?