排班:如何强制员工的班次连续进行

huangapple go评论56阅读模式
英文:

Scheduling : How to enforce consecutiveness of shifts for an employee

问题

我有一个排班问题,其中员工一天最多可以工作4小时,总共允许的工作时间是8小时。这4小时要求连续工作。

例如,决策变量(二进制)是员工是否会在特定小时工作。因此,一个示例解决方案可能如下所示:

小时:0 1 2 3 4 5 7 [一天8小时]
dv  :0 1 1 1 1 0 0 [员工从第2小时工作到第5小时]

一个开源求解器中的解决方案/方法真的会对我有所帮助。

英文:

I have a scheduling problem where an employee can work maximum of say, 4 hours out of total allowable 8 hours in a day. For these 4 hours, I want them to be consecutive.

For example, decision variables (binary) are whether an employee will work in a particular hour or not. So a sample solution could look like below:

hrs : 0 1 2 3 4 5 7  [8 hours in a day]
dv  : 0 1 1 1 1 0 0  [employee works from 2nd hour through 5th hour]

A solution / approach in an open-source solver would really help me.

答案1

得分: 1

允许最多一次变动以限制启动次数。启动发生在我们从 dv[t-1]=0 转变为 dv[t]=1 时。

 start[t] >= dv[t] - dv[t-1]   对于所有 t
 sum(t,start[t]) <= 1
 start[t] ∈ {0,1} (或者 ∈ [0,1])

这将给您一个启动,因此一个变动。这是一种非常常见的表述,在调度中经常使用。值得知道。

英文:

To allow a maximum of one shift restrict the number of starts. A start happens when we go from dv[t-1]=0 to dv[t]=1.

 start[t] >= dv[t] - dv[t-1]   for all t
 sum(t,start[t]) <= 1
 start[t] ∈ {0,1} (or even ∈ [0,1])

This will give you just one start, so one shift. This is a fairly standard formulation that is used very often in scheduling. Worthwhile to know.

答案2

得分: 0

这可以被表达为一个混合整数线性规划问题。我的方法如下:

  1. 创建员工 (e) 按一天中的小时 (t) 的二进制变量,set

  2. 计算班次变动,并将其存储在一个变量中,例如 wet,如下所示:
    wet = set - se(t-1),对于 t > 0, e

  3. we0 = se0,对所有 e

  4. 创建一个二进制变量,用于捕获班次的开始时间,称之为 vet,设置以下约束条件:
    vet = max(0, wet),对所有 e, t

  5. 我们希望班次只能开始一次,因此强制执行以下约束条件:
    对所有 e,求和(vet) ≤ 1

以下是在Google-OR-tool的线性求解器中实现上述内容的代码。请注意,我们将不得不线性化max函数。

from ortools.linear_solver import pywraplp
s = pywraplp.Solver("", pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)

班次长度 = 8

# 决策变量:班次是否被工作
# 根据上述解释,s_t 我只取了一个员工来简化
shift_bool = 
展开收缩
# 约束条件:员工最多能工作 4 小时 s.Add(sum(shift_bool) == 4) # 决策变量:捕获从一小时到另一小时的班次变动 # 根据上述解释,w_t shift_diff =
展开收缩
for i in range(班次长度 - 1): s.Add(shift_diff[i+1] == shift_bool[i+1] - shift_bool[i]) # 对于第一个小时: s.Add(shift_diff[0] == shift_bool[0]) # 决策变量:捕获班次开始 # 根据上述解释,v_t shift_max =
展开收缩
s.Add(sum(shift_max) == 1) # 我们需要线性化 `max` 操作,所以设 u = max(a0, a1, a2) # u >= a_i, 对所有 i # u <= a_i + (1 - b_i) * M, 对所有 i # 求和(b_i) = 1 # b_i 是二进制变量 # M 是 u 的上限 # 我在下面实现了上述逻辑: # 实质上,对于每个小时,我们需要在 0 和 shift_diff 之间取最大值 for i in range(班次长度): bool_var =
展开收缩
s.Add(shift_max[i] >= shift_diff[i]) s.Add(shift_max[i] >= 0) s.Add(shift_max[i] <= shift_diff[i] + (1 - bool_var[0]) * 5) s.Add(shift_max[i] <= 0 + (1 - bool_var[0]) * 5) s.Add(sum(bool_var) == 1) # 假目标 obj = sum(shift_bool) s.Minimize(obj) s.Solve() # shift_assigned 的值: [shift_bool[i].SolutionValue() for i in range(班次长度)] # [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]

只是随意玩一下,如果你添加一个额外的约束:s.Add(shift_bool[0] + shift_bool[1] == 0),那么解决方案将如下所示:[0, 0, 0, 1, 1, 1, 1, 0]

英文:

This can be formulated as a mixed integer linear program. My approach is as follows:

  1. Create binary variables by employee (e) by hour of the day (t), s<sub>et</sub>

  2. Calculate shift changes, and store them in a variable, say, w<sub>et</sub>, so:
    w<sub>et</sub> = s<sub>et</sub> - s<sub>et-1</sub>, for t > 0, e

  3. w<sub>e0</sub> = s<sub>e0</sub>, for all e

  4. Create binary variable, that will capture when the shift will start, say, v<sub>et</sub>, put the following constraint:
    v<sub>et</sub> = max(0, w<sub>et</sub>), for all e, t

  5. we want shift to only start once, so enforce the following constraint:
    sum_over_t(v<sub>et</sub>) <= 1, for all e

Below is the implementation of the above in Google-OR-tool's linear solver. Please note that we will have to linearise the max function.

from ortools.linear_solver import pywraplp
s = pywraplp.Solver(&quot;&quot;, pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)

shift_length = 8

# decision variable : whether the shift was worked or not
# s_t following above explanation. I have taken 1 employee for simplicity
shift_bool = 
展开收缩
# constraint : employee can work for max 4 hours s.Add(sum(shift_bool) == 4) # decision variable : capture shift changes from one hour to another. # w_t following above explanation. shift_diff =
展开收缩
for i in range(shift_length - 1): s.Add(shift_diff[i+1] == shift_bool[i+1] - shift_bool[i]) # for first hour: s.Add(shift_diff[0] == shift_bool[0]) # decision variable : to capture shift start # v_t following above explanation shift_max =
展开收缩
s.Add(sum(shift_max) == 1) # we need to linearise the `max` operation, so say, u = max(a0, a1, a2) # u &gt;= a_i, for all i # u &lt;= a_i + (1 - b_i) * M, for all I # sum_over_i(b_i) = 1 # b_i is binary variable # M is the upper bound on u # I have implemented the above logic below: # essentially, for each hour we need to take max between # 0 and shift_diff for i in range(shift_length): bool_var =
展开收缩
s.Add(shift_max[i] &gt;= shift_diff[i]) s.Add(shift_max[i] &gt;= 0) s.Add(shift_max[i] &lt;= shift_diff[i] + (1 - bool_var[0]) * 5) s.Add(shift_max[i] &lt;= 0 + (1 - bool_var[0]) * 5) s.Add(sum(bool_var) == 1) # dummy objective obj = sum(shift_bool) s.Minimize(obj) s.Solve() # shift_assigned values: [shift_bool[i].SolutionValue() for i in range(shift_length)] # [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]

just to play around, if you add an additional constraint : s.Add(shift_bool[0] + shift_bool[1] == 0), then the solution looks as follows: [0, 0, 0, 1, 1, 1, 1, 0]

huangapple
  • 本文由 发表于 2023年7月13日 20:35:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76679455.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定