MIP Python学校教师时间表问题-如何检查等于零的变量数量是否等于某个数字



我正试图解决一个学校教师时间问题。我正处于一个很好的阶段,但现在我陷入了如何建模每个老师一周有一定数量的空闲日的问题,例如,每个老师零课的天数之和等于某个数字。

基本问题是:我如何添加一个contint来计算变量等于0.0的时间?

y数数老师每天教的小时数。或多或少应该是这样的(但当然不起作用(

for t in teacher:
model += xsum( y[t, d] == 0.0 for d in day ) == 1.0

我还尝试使用变量的实际值:

for t in teacher:
model += xsum( y[t, d].x == 0.0 for d in day ) == 1.0

和:

for t in teacher:
model += xsum( y[t, d] for d in day if y[t, d] == 0.0 ) == 1.0

但似乎都不起作用。

我可以分享一些代码,但实际上所有的代码都是用意大利语写的,所以我试图直接归结为要点。

假设您想用(线性(MIP解算器解决此问题,则您的尝试违反了线性。以下是您可以尝试的:

y[t,d]为教师td天的小时数。假设它是一个整数变量。然后我们可以定义二进制变量:

x[t,d] = 1  if t works at d
= 0  is t is off at d

x和y之间的链接可以建模为:

x[t,d] <= y[t,d]     (y[t,d]=0 => x[t,d]=0)
8*x[t,d] >= y[t,d]   (y[t,d]>0 => x[t,d]=1, assuming 8 hours is the maximum per day)  

现在我们可以说(在伪代码中(:

sum(d, 1-x[t,d]) = 1   (exactly one day off)

或者:

sum(d, x[t,d]) = 4     (exactly 4 days at work)

@erwin解决方案是从理论POV开始工作的,所以我接受它,但我不能在MIP Python代码中进行强制转换。

所以我找到了一些其他的解决方案,在某些方面并不完美,但我把它放在这里供其他伴侣参考。当然,一旦我翻译成英语,我就会分享代码。顺便说一句,我正在学术界寻找一位学术团队合作伙伴,写一篇科学文章(@erwin?(或一篇媒介文章,或两者兼而有之,并以开源方式发布所有代码。

让我们回到问题上来。我找到了3个硬成本解决方案和1个软成本解决方案。我展示了所有的解决方案,以便有人可以使用它

HC解决方案1-优雅的

我可以简单地添加一个硬成本,y[t,d]<=0.0表示老师的组合,我想休息的一周中的哪一天。

for t in self.teacher:
for d in self.day:
if t,d in self.desiderata:
self.model += self.y[t, d] <= 0.0

(顺便说一句,这是一个简化的版本,因为desiderata是一个结构化的决策,但我认为你明白了(

切中要害,效果非常好!

但我知道可以做得更好,我也可以避免使用y[t,d],因为它是一个导数变量,所以我可以避免它并提高性能。

HC解决方案2-更好的性能解决方案

因为y[t,d]实际上是原始变量x[t,g,m,d,h]的衍生变量,其中:

self.x = {
(t, g, m, d, h): self.model.add_var(
name="x({},{},{},{},{})".format(t, g, m, d, h),
var_type=BINARY
)
for t in self.teacher
for g in self.grade
for m in self.matter
for d in self.day
for h in self.hour
}

和:

self.y = {
(t, d): self.model.add_var(
name="y({},{})".format(t, d),
var_type=BINARY
)
for t in self.teacher
for d in self.day
}
for t in self.teacher:
for d in self.day:
self.y[t,d] = xsum( self.x[t, g, m, d, h] 
for h in self.hour
for m in self.matter
for g in self.grade)

因此,基本上,y[t,d]求出教师t在一天d工作的时间。实际上,我不需要y[t,d],因为我可以用这种方式直接写约束:

for t in teacher:
for d in self.day:
if t,d in self.desiderata:
self.model += xsum(self.x[t, g, m, d, h] 
for g in self.grade 
for m in self.matter 
for h in self.hour) <= 0.0

而且。。。。。天哪,这也行!这是一个了不起的

这应该会带来更好的性能,因为NP完全中的问题减少了变量数量,从而减少了解决的时间。

但我相信我能改进它。为什么我在老师休息的地方加上变量x[t,g,m,d,h]作为t,d的组合?如果我不生成x[t,g,m,d,h],那么默认情况下,问题会在没有任何其他约束的情况下得到解决。让我们试试:

HC溶液3-更快的

基本上,变量x的生成考虑了每位教师的休息日:

self.x = {
(t, g, m, d, h): self.model.add_var(
name="x({},{},{},{},{})".format(t, g, m, d, h),
var_type=BINARY
)
for t in self.teacher
for g in self.grade
for m in self.matter
for d in self.day
for h in self.hour
if not (t,d in self.desiderata) # Each teacher not work in days off
}

而且在不添加任何其他约束的情况下,这也很有效!!!开箱即用!

这让我发现18%的变量没有生成。记住,NP完全问题,所以这是一个巨大的改进!

因此,我有三种不同的完全工作的硬约束解决方案,但有一个问题,因为如果休息日冲突太多,这种方法很容易导致不可行的解决方案。

有了代码,我可以一天又一天地放松,但这种方式不再是纯粹的MIP问题。

为什么不使用目标函数?

因此,让我们在目标函数中插入匹配的天数。

SC-最大限度地利用

为了清楚起见,让我们使用y[t,d],但考虑一下,如果需要的话,我们可以去掉它,改为使用x

self.model.objective = minimize( 
xsum( self.y[t,d] for t in self.teacher for d in self.day if t,d in self.desiderata )
)

而且效果也很好!!!

在我将尽快分享的代码中,有一些代码优化主要是为了减少变量的数量,但概念在其他方面有所表达。

谢谢你阅读这么长的回复。

最新更新