Решение задачи присваивания с конкретными ограничениями - PullRequest
1 голос
/ 23 мая 2019

Представьте себе следующие данные (код для воспроизведения всех выходных данных находится в конце):

df

           cars horsepower year safety
1        Toyota        140 2008      4
2      Chrysler        120 2009      4
3          Ford        140 2010      5
4           BMW        150 2008      3
5 Mercedes-Benz        150 2008      3
6       Hyundai        120 2009      4
7        Jaguar        150 2007      3
8         Tesla        120 2010      5

Я бы хотел поменять местами машины, чтобы получить что-то вроде:

   cars_initial    cars_match horsepower year safety horsepowerMatch yearMatch safetyMatch
1        Toyota           BMW        140 2008      4             150      2008           3
2         Tesla      Chrysler        120 2010      5             120      2009           4
3 Mercedes-Benz          Ford        150 2008      3             140      2010           5
4        Jaguar       Hyundai        150 2007      3             120      2009           4
5       Hyundai        Jaguar        120 2009      4             150      2007           3
6          Ford Mercedes-Benz        140 2010      5             150      2008           3
7      Chrysler         Tesla        120 2009      4             120      2010           5
8           BMW        Toyota        150 2008      3             140      2008           4

Теперь это типичная проблема назначения, которая в вышеописанном случае решалась случайным образом, т. Е. По матрице затрат во всех случаях равной 0.

Меня интересуют результаты.В приведенном выше случае решение дает следующую статистику:

stats

  horsepower year safety
1       0.25 0.25      0

То есть 1/4 свопов имели равную мощность и т. Д.

Вот мой вопрос: какрешать такие задания, устанавливая ограничения на то, что именно должно быть статистикой результатов напрямую, без метода проб и ошибок с установкой затрат?

Например, что, если я хотел бы иметь решение, где safety имеет более 0,20 совпадений и year не менее 0,10, как показано ниже?

desiredOutput

   cars_initial    cars_match
1        Toyota      Chrysler
2         Tesla Mercedes-Benz
3 Mercedes-Benz           BMW
4        Jaguar        Toyota
5       Hyundai         Tesla
6          Ford       Hyundai
7      Chrysler        Jaguar
8           BMW          Ford

statsDesired

  horsepower year safety
1       0.25 0.12   0.25

Конечно, я мог бы просто установить матрицу затрат на меньшее число во всех случаях, когда safety автомобилейравно.

Но есть ли способ повлиять на результат, напрямую установив ограничение на то, какой должна быть статистика результатов?

Возможно, есть способ оптимизировать затраты, чтобы достичь желаемогорезультат?

Код:

library(lpSolve)
library(dplyr)
library(tidyr)

set.seed(1)

df <- data.frame(
  cars = c("Toyota", "Chrysler", "Ford", "BMW", "Mercedes-Benz", "Hyundai", "Jaguar", "Tesla"),
  horsepower = c(140, 120, 140, 150, 150, 120, 150, 120),
  year = c(2008, 2009, 2010, 2008, 2008, 2009, 2007, 2010),
  safety = c(4, 4, 5, 3, 3, 4, 3, 5)
)

mat <- df %>% select(cars) %>%
  crossing(df %>% select(cars)) %>%
  mutate(val = 0) %>% 
  spread(cars, val)

solved <- lp.assign(mat %>% select(-cars1) %>% as.matrix())$solution

matches <- as.data.frame(solved) %>%
  setNames(., names(mat %>% select(-cars1))) %>%
  bind_cols(mat %>% select(cars1)) %>%
  gather(key, val, -cars1) %>%
  filter(val == 1) %>% select(-val, cars_initial = cars1, cars_match = key)

nms <- c("cars", paste0(names(df %>% select(-cars)), "Match"))

matches <- matches %>%
  left_join(df, by = c("cars_initial" = "cars")) %>%
  left_join(df %>% setNames(., nms), by = c("cars_match" = "cars"))

stats <- matches %>%
  summarise(
    horsepower = round(sum(horsepower == horsepowerMatch) / n(), 2),
    year = round(sum(year == yearMatch) / n(), 2),
    safety = round(sum(safety == safetyMatch) / n(), 2)
  )

desiredOutput <- data.frame(cars_initial = matches$cars_initial, cars_match = c("Chrysler", "Mercedes-Benz", "BMW", "Toyota", "Tesla", "Hyundai", "Jaguar", "Ford"))

statsDesired <- desiredOutput %>%
  left_join(df, by = c("cars_initial" = "cars")) %>%
  left_join(df %>% setNames(., nms), by = c("cars_match" = "cars")) %>%
  summarise(
    horsepower = round(sum(horsepower == horsepowerMatch) / n(), 2),
    year = round(sum(year == yearMatch) / n(), 2),
    safety = round(sum(safety == safetyMatch) / n(), 2)
  )

Надеюсь, приведенных выше примеров достаточно, это мой первый вопрос, поэтому, пожалуйста, дайте мне знать, если мне нужно предоставить что-то еще.

Код находится в R, но я также добавил тег Python, так как я не особо обращаю внимание на язык возможных решений.

1 Ответ

0 голосов
/ 24 мая 2019

Вот частичная формулировка этой проблемы как задачи целочисленного программирования (IP).

Пусть I будет набором типов автомобилей.Для типов автомобилей i и j в I:

  • h[i,j] = 1, если автомобили i и j имеют одинаковую мощность
  • y[i,j] = 1, если автомобили i и j имеют один и тот же год
  • и аналогично для s[i,j] (безопасность)

Это параметры , означая входные данные для вашей модели.(Вам потребуется написать код для вычисления этих двоичных величин на основе таблицы данных.)

Теперь введите следующие переменные решения , т. Е. Переменные, для которых ваша модель IP будет выбирать значения:

  • x[i,j] = 1, если мы назначим тип автомобиля j в качестве совпадения типа i

Теперь, как правило, IP имеет целевую функцию, котораямы хотим минимизировать или максимизировать.В этом случае нет целевой функции - вы просто хотите найти набор совпадений, который удовлетворяет вашим ограничениям.Таким образом, ваша целевая функция может быть просто:

minimize 0

Вот первое ограничение.Это говорит: по крайней мере a спичек должны иметь одинаковую мощность.(a - это дробная часть.) С левой стороны указано количество совпадений, имеющих одинаковую мощность: Для каждой пары типов автомобилей i и j, если j назначено как i 's соответствуют и они имеют одинаковую мощность, считайте 1;в противном случае посчитайте 0. Правая часть - это количество совпадений, которое вы хотите, т. е. a доля всего набора.

subject to sum {i in I, j in I} h[i,j] * x[i,j] >= a * |I|

Теперь сформулируйте аналогичные ограничения для других категорий.

Далее вам нужно ограничение, которое говорит, что каждому типу машины i должен быть присвоен ровно один тип машины j:

subject to sum {j in I} x[i,j] == 1 for all i in I

Наконец, вам нужны ограничения, говорящие о том, что переменные решенияДвоичный файл:

subject to x[i,j] in {0,1} for all i, j in I

Теперь для решения этой проблемы вам потребуется либо использовать язык математического моделирования, такой как AMPL или GAMS, либо пакет, подобный PuLP для Python.

Надеюсь, это поможет.Я мог откусить больше, чем ты можешь здесь жевать.

...