关于 or-tools shift scheduling example 的概念性问题
Conceptual question about or-tools shift scheduling example
我对这个调度示例有一个概念性问题:
https://github.com/google/or-tools/blob/master/examples/python/shift_scheduling_sat.py
它将域问题建模为布尔标志的 3 维张量 [员工、班次、天],指示特定员工在特定日期的特定班次分配。
关于轮班本身的信息很少(除了是休息日、上午、下午还是夜班)。
然后它使用 add_soft_sequence_constraint
和 negated_bounded_span
,例如,将连续休息天数限制为最多 1 天或 2 天。
对于我的问题,我有未预定义的可变班次长度——解决方案的一个关键部分是每次班次的长度。一般的想法是全天都有可变的覆盖要求,例如早上 8 点到 10 点只需要 1 名员工,上午 10 点到下午 4 点需要 2 名员工,下午 4 点到晚上 8 点需要 1 名员工。还有一份员工清单和他们的要求:他们每天的最少和最多工作时间是多少,他们可以被分配到的最短和最长轮班时间,他们每天可以被分配到的最大班次数量,他们之间的最短时间轮班等。不同员工的轮班可以完全或部分重叠。
我可以用类似于上述示例的类似方式对此进行建模,其中我通过将每天分成 24 个时间 windows 来增加轮班维度的大小,每个时间 1 小时。员工被分配到这些固定时间 windows。移位是分配给 True 的连续变量序列。为了使轮班符合一些常识(例如,没有人在早上进来 1 小时,然后在午夜前再进来 1 小时),我可以使用与上面示例中类似的方法,我需要一定的最小和最大轮班持续时间(分配给员工的连续时间windows)。
这可行,但感觉力适合这个 is-this-employee-assigned-to-this-shift 布尔张量模型。
我的问题是——是否有更自然的方法使用整数、区间或其他一些东西来对约束进行建模,例如建模为每天的班次列表,其中“班次”是开始和结束时间以及分配的员工?
当约束主要是可接受的每日轮班顺序时,此问题针对固定轮班进行了调整。
不适用于变速模型。
为了汇总在特定时间值班的员工人数以检查需求满足情况,您需要了解每个员工当时是否在值班--这是基本上是 [employee, shift, day] 的布尔数组。我认为无论您做什么,您的模型中都需要该数组。
您还可以引入一个 IntervalVar
列表,代表每个员工的轮班,以便更容易地编写 max/min 轮班持续时间和轮班之间的休息时间的约束,但您将有将它们与具有附加约束的布尔数组相关联。
我用pseudo-code的回答,其实我并没有实现。
轮班时长和下班时间的限制类似于强制执行此 pseudo-code 中的表达式(仅在轮班处于活动状态时强制执行):
(shift.duration <= maxShiftLength).OnlyEnforceIf(active)
(shift.duration >= minShiftLength).OnlyEnforceIf(active)
(shift[i].end + minOffTime <= shift[i+1].start).OnlyEnforceIf(active[i+1])
由于您需要为每个员工创建比实际分配的更多的潜在班次,因此您需要一个标志来确定班次是否实际有效。
对于不活跃的轮班,强制执行
(shift.start == 0).OnlyEnforceIf(active.Not())
(shift.end == 0).OnlyEnforceIf(active.Not())
可以通过为每一天设置一个(常数)IntervalVar day
,然后对表达式求和来计算每天最长小时数的限制
max(0,min(shift.end, day.end) - max(shift.start, day.start))
对于当天每个员工的所有班次,并将总和限制为小于限制。为每一天和员工执行此操作。
用连续的小时来测量时间比使用两个索引 shift 和天更简单,所以你会有一个布尔数组 onDuty[employee, hour]
。小时的值将从 0 到您的计划范围。将这些与 IntervalVar
列表相关联的约束会更容易。
如果
,给定班次在时间 h 值班
min((shift.start >= h), (shift.end <h), active)
制定不等式,以便从 0 到 1 的区间在第 0 小时而不是第 1 小时有效。
如果此值在其所有轮班中的最大值为 1,则给定员工在时间 h 值班。
onDuty[e, h] == max((min((shift[i].start >= h), (shift[i].end <h), active[i]) for all i for the employee e)
最后一条评论:仅仅作为一个多维数组并不能使布尔数组成为张量——参见 https://en.wikipedia.org/wiki/Tensor。
我对这个调度示例有一个概念性问题:
https://github.com/google/or-tools/blob/master/examples/python/shift_scheduling_sat.py
它将域问题建模为布尔标志的 3 维张量 [员工、班次、天],指示特定员工在特定日期的特定班次分配。
关于轮班本身的信息很少(除了是休息日、上午、下午还是夜班)。
然后它使用 add_soft_sequence_constraint
和 negated_bounded_span
,例如,将连续休息天数限制为最多 1 天或 2 天。
对于我的问题,我有未预定义的可变班次长度——解决方案的一个关键部分是每次班次的长度。一般的想法是全天都有可变的覆盖要求,例如早上 8 点到 10 点只需要 1 名员工,上午 10 点到下午 4 点需要 2 名员工,下午 4 点到晚上 8 点需要 1 名员工。还有一份员工清单和他们的要求:他们每天的最少和最多工作时间是多少,他们可以被分配到的最短和最长轮班时间,他们每天可以被分配到的最大班次数量,他们之间的最短时间轮班等。不同员工的轮班可以完全或部分重叠。
我可以用类似于上述示例的类似方式对此进行建模,其中我通过将每天分成 24 个时间 windows 来增加轮班维度的大小,每个时间 1 小时。员工被分配到这些固定时间 windows。移位是分配给 True 的连续变量序列。为了使轮班符合一些常识(例如,没有人在早上进来 1 小时,然后在午夜前再进来 1 小时),我可以使用与上面示例中类似的方法,我需要一定的最小和最大轮班持续时间(分配给员工的连续时间windows)。
这可行,但感觉力适合这个 is-this-employee-assigned-to-this-shift 布尔张量模型。
我的问题是——是否有更自然的方法使用整数、区间或其他一些东西来对约束进行建模,例如建模为每天的班次列表,其中“班次”是开始和结束时间以及分配的员工?
当约束主要是可接受的每日轮班顺序时,此问题针对固定轮班进行了调整。
不适用于变速模型。
为了汇总在特定时间值班的员工人数以检查需求满足情况,您需要了解每个员工当时是否在值班--这是基本上是 [employee, shift, day] 的布尔数组。我认为无论您做什么,您的模型中都需要该数组。
您还可以引入一个 IntervalVar
列表,代表每个员工的轮班,以便更容易地编写 max/min 轮班持续时间和轮班之间的休息时间的约束,但您将有将它们与具有附加约束的布尔数组相关联。
我用pseudo-code的回答,其实我并没有实现。
轮班时长和下班时间的限制类似于强制执行此 pseudo-code 中的表达式(仅在轮班处于活动状态时强制执行):
(shift.duration <= maxShiftLength).OnlyEnforceIf(active)
(shift.duration >= minShiftLength).OnlyEnforceIf(active)
(shift[i].end + minOffTime <= shift[i+1].start).OnlyEnforceIf(active[i+1])
由于您需要为每个员工创建比实际分配的更多的潜在班次,因此您需要一个标志来确定班次是否实际有效。 对于不活跃的轮班,强制执行
(shift.start == 0).OnlyEnforceIf(active.Not())
(shift.end == 0).OnlyEnforceIf(active.Not())
可以通过为每一天设置一个(常数)IntervalVar day
,然后对表达式求和来计算每天最长小时数的限制
max(0,min(shift.end, day.end) - max(shift.start, day.start))
对于当天每个员工的所有班次,并将总和限制为小于限制。为每一天和员工执行此操作。
用连续的小时来测量时间比使用两个索引 shift 和天更简单,所以你会有一个布尔数组 onDuty[employee, hour]
。小时的值将从 0 到您的计划范围。将这些与 IntervalVar
列表相关联的约束会更容易。
如果
,给定班次在时间 h 值班min((shift.start >= h), (shift.end <h), active)
制定不等式,以便从 0 到 1 的区间在第 0 小时而不是第 1 小时有效。
如果此值在其所有轮班中的最大值为 1,则给定员工在时间 h 值班。
onDuty[e, h] == max((min((shift[i].start >= h), (shift[i].end <h), active[i]) for all i for the employee e)
最后一条评论:仅仅作为一个多维数组并不能使布尔数组成为张量——参见 https://en.wikipedia.org/wiki/Tensor。