Python 求解具有特定约束的指派问题

Python 求解具有特定约束的指派问题,python,r,graph-theory,mathematical-optimization,lpsolve,Python,R,Graph Theory,Mathematical Optimization,Lpsolve,想象以下数据(复制所有输出的代码位于末尾): 我想把车换成这样: cars_initial cars_match horsepower year safety horsepowerMatch yearMatch safetyMatch 1 Toyota BMW 140 2008 4 150 2008 3 2 Tesla Chrysler

想象以下数据(复制所有输出的代码位于末尾):

我想把车换成这样:

   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
当然,我可以将成本矩阵设置为一个较低的数值,在所有情况下,汽车的
安全性
是相等的

但是有没有一种方法可以通过直接设置结果统计的约束来影响结果呢

也许有一种方法可以优化成本,以达到预期的结果

守则:

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)
  )
库(lpSolve)
图书馆(dplyr)
图书馆(tidyr)
种子(1)
df%
交叉口(df%%>%选择(车辆))%%>%
变异(val=0)%>%
排列(车辆,val)
已解决%select(-cars1)%%>%as.matrix())$solution
匹配%
集合名称(,,名称(mat%%>%select(-cars1)))%%>%
绑定列(mat%%>%选择(cars1))%%>%
聚集(键,val,-cars1)%>%
筛选器(val==1)%>%select(-val,cars\u initial=cars1,cars\u match=key)
nms%选择(-cars)),“匹配”))
匹配%
左连接(df,by=c(“车首字母”=“车”))%>%
左连接(df%>%setNames(,nms),by=c(“cars\u match”=“cars”))
统计数据%
总结(
马力=四舍五入(总和(马力==马力匹配)/n(),2),
年=四舍五入(总和(年==年匹配)/n(),2),
安全=四舍五入(总和(安全==安全匹配)/n(),2)
)
期望输出%
左连接(df%>%setNames(,nms),by=c(“汽车匹配”=“汽车”))%>%
总结(
马力=四舍五入(总和(马力==马力匹配)/n(),2),
年=四舍五入(总和(年==年匹配)/n(),2),
安全=四舍五入(总和(安全==安全匹配)/n(),2)
)
我希望上面的例子是足够的,这是我的第一个问题,所以请让我知道,如果我需要提供更多的东西


代码在
R
中,但我也添加了标记
Python
,因为我并不介意可能的解决方案的语言。

这里是这个问题作为整数编程(IP)问题的部分公式

I
成为汽车类型的集合。对于
i
中的
i
j
车型,让:

  • 如果车辆
    i
    j
    具有相同的马力,则
    h[i,j]
    =1
  • 如果车辆
    i
    j
    具有相同的年份,则
    y[i,j]
    =1
  • 同样,对于
    s[i,j]
    (安全性)
这些是参数,表示模型的输入。(您需要编写代码,根据数据表计算这些二进制数量。)

现在介绍以下决策变量,即IP模型将选择其值的变量:

  • x[i,j]
    =1,如果我们将车型
    j
    指定为车型
    i
    的匹配项
现在,通常IP有一个目标函数,我们希望最小化或最大化。在这种情况下,没有目标函数——您只想找到一组满足约束的匹配项。所以你的目标函数可以是:

minimize 0
这是第一个限制。上面写着:至少有一个火柴必须有相同的马力。(
a
是一个分数。)左侧是具有相同马力的匹配数:对于每对
i
j
,如果
j
被指定为
i
的匹配,并且它们具有相同的马力,则计算a 1;否则,计数为0。右边是您想要的匹配数,即整个集合的一小部分

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)或Python的软件包(如
pill


我希望这有帮助。我可能会咬得比你在这里嚼得更多

欢迎来到SO!让我确定我正确理解了你的问题。您的意思是:对于每种车型,选择一个匹配项,这样至少有
a
%的车对彼此具有相同的马力,至少有
b
%的车对彼此具有相同的年份,并且至少有
c
%的车对彼此具有相同的安全性?谢谢!没错,这是一个很好的公式。我会把它公式化为一个整数规划问题。你对IP有多熟悉?我必须承认一点也不熟悉!你有类似类型的问题和解决方法的例子吗?如果是这样的话,这将是一个全新的领域供你学习。我会给你一个解决这个问题的公式,你可以决定是继续还是尝试另一种方法。非常感谢!这确实应该让我开始。我会等一等,看看是否还有其他答案,否则我接受你的答案。不用担心,这是完全好的。同意这是一个很好的适合IP。值得注意的是,在这个问题的构造中,当您对结果应用约束时,您可能会得到满足您的标准的第一个答案,而不是最佳/最佳答案,因为您没有目标函数。此外,还不清楚如何设置这些参数
subject to x[i,j] in {0,1} for all i, j in I