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

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

Scheduling : How to enforce consecutiveness of shifts for an employee

问题

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

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

  1. 小时:0 1 2 3 4 5 7 [一天8小时]
  2. 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:

  1. hrs : 0 1 2 3 4 5 7 [8 hours in a day]
  2. 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 时。

  1. start[t] >= dv[t] - dv[t-1] 对于所有 t
  2. sum(t,start[t]) <= 1
  3. 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.

  1. start[t] >= dv[t] - dv[t-1] for all t
  2. sum(t,start[t]) <= 1
  3. 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函数。

  1. from ortools.linear_solver import pywraplp
  2. s = pywraplp.Solver("", pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)
  3. 班次长度 = 8
  4. # 决策变量:班次是否被工作
  5. # 根据上述解释,s_t 我只取了一个员工来简化
  6. shift_bool =
    展开收缩
  7. # 约束条件:员工最多能工作 4 小时
  8. s.Add(sum(shift_bool) == 4)
  9. # 决策变量:捕获从一小时到另一小时的班次变动
  10. # 根据上述解释,w_t
  11. shift_diff =
    展开收缩
  12. for i in range(班次长度 - 1):
  13. s.Add(shift_diff[i+1] == shift_bool[i+1] - shift_bool[i])
  14. # 对于第一个小时:
  15. s.Add(shift_diff[0] == shift_bool[0])
  16. # 决策变量:捕获班次开始
  17. # 根据上述解释,v_t
  18. shift_max =
    展开收缩
  19. s.Add(sum(shift_max) == 1)
  20. # 我们需要线性化 `max` 操作,所以设 u = max(a0, a1, a2)
  21. # u >= a_i, 对所有 i
  22. # u <= a_i + (1 - b_i) * M, 对所有 i
  23. # 求和(b_i) = 1
  24. # b_i 是二进制变量
  25. # M 是 u 的上限
  26. # 我在下面实现了上述逻辑:
  27. # 实质上,对于每个小时,我们需要在 0 和 shift_diff 之间取最大值
  28. for i in range(班次长度):
  29. bool_var =
    展开收缩
  30. s.Add(shift_max[i] >= shift_diff[i])
  31. s.Add(shift_max[i] >= 0)
  32. s.Add(shift_max[i] <= shift_diff[i] + (1 - bool_var[0]) * 5)
  33. s.Add(shift_max[i] <= 0 + (1 - bool_var[0]) * 5)
  34. s.Add(sum(bool_var) == 1)
  35. # 假目标
  36. obj = sum(shift_bool)
  37. s.Minimize(obj)
  38. s.Solve()
  39. # shift_assigned 的值:
  40. [shift_bool[i].SolutionValue() for i in range(班次长度)]
  41. # [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.

  1. from ortools.linear_solver import pywraplp
  2. s = pywraplp.Solver(&quot;&quot;, pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)
  3. shift_length = 8
  4. # decision variable : whether the shift was worked or not
  5. # s_t following above explanation. I have taken 1 employee for simplicity
  6. shift_bool =
    展开收缩
  7. # constraint : employee can work for max 4 hours
  8. s.Add(sum(shift_bool) == 4)
  9. # decision variable : capture shift changes from one hour to another.
  10. # w_t following above explanation.
  11. shift_diff =
    展开收缩
  12. for i in range(shift_length - 1):
  13. s.Add(shift_diff[i+1] == shift_bool[i+1] - shift_bool[i])
  14. # for first hour:
  15. s.Add(shift_diff[0] == shift_bool[0])
  16. # decision variable : to capture shift start
  17. # v_t following above explanation
  18. shift_max =
    展开收缩
  19. s.Add(sum(shift_max) == 1)
  20. # we need to linearise the `max` operation, so say, u = max(a0, a1, a2)
  21. # u &gt;= a_i, for all i
  22. # u &lt;= a_i + (1 - b_i) * M, for all I
  23. # sum_over_i(b_i) = 1
  24. # b_i is binary variable
  25. # M is the upper bound on u
  26. # I have implemented the above logic below:
  27. # essentially, for each hour we need to take max between
  28. # 0 and shift_diff
  29. for i in range(shift_length):
  30. bool_var =
    展开收缩
  31. s.Add(shift_max[i] &gt;= shift_diff[i])
  32. s.Add(shift_max[i] &gt;= 0)
  33. s.Add(shift_max[i] &lt;= shift_diff[i] + (1 - bool_var[0]) * 5)
  34. s.Add(shift_max[i] &lt;= 0 + (1 - bool_var[0]) * 5)
  35. s.Add(sum(bool_var) == 1)
  36. # dummy objective
  37. obj = sum(shift_bool)
  38. s.Minimize(obj)
  39. s.Solve()
  40. # shift_assigned values:
  41. [shift_bool[i].SolutionValue() for i in range(shift_length)]
  42. # [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:

确定