我正试图解决一个学校教师时间问题。我正处于一个很好的阶段,但现在我陷入了如何建模每个老师一周有一定数量的空闲日的问题,例如,每个老师零课的天数之和等于某个数字。
基本问题是:我如何添加一个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]
为教师t
在d
天的小时数。假设它是一个整数变量。然后我们可以定义二进制变量:
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 )
)
而且效果也很好!!!
在我将尽快分享的代码中,有一些代码优化主要是为了减少变量的数量,但概念在其他方面有所表达。
谢谢你阅读这么长的回复。