员工轮班安排
- 模型分解
- 模型集
- 模型参数
- 模型决策变量
- 模型目标
- 模型约束
- Pyomo实现
- 排班方案可视化展示
- 工人排版表
大学校园内新开了一家食品店,每周 7 天、每天 24 小时营业。
- 每天有三个八小时班次
- 早班从 6:00 到 14:00,晚班从 14:00 到 22:00,夜班从 22:00 到第二天早上 6:00
- 夜间只有一名工人,白天有两名工人,但周日每班只有一名工人
- 每位工人每周最多工作时间不超过 40 小时,两班之间必须休息 12 小时
- 至于每周的休息日,周日休息的员工也希望周六休息
模型分解
模型集
每天有三班,每周七天。这些观察结果表明需要三个有序集合:
- W是一组N代表工人的元素
- D是包含一周7天的集合
- S是每天三班的安排
添加附加集来提高模型的可读性:
- T是有序的(天,班次)对的集合,描述一周内所有可用的班次
- B是一周内所有重叠24小时时段的有序集合,该集合的一个元素包含相应时段内的(日、班次)时段
- E是周末所有 (日、班次) 对的集合,此集合将用于实现工人对周末排班的偏好
模型参数
- N 工人的数量
- R d , s R_{d,s} Rd,s 白班所需的最少工人数量(d,s)
模型决策变量
a w , d , s = { 1 工人w被分配到某个班次,(d,s) in T 0 otherwise a_{w,d,s} = \begin{cases} 1 & \text{工人w被分配到某个班次,(d,s) in T} \\ 0 & \text{otherwise} \end{cases} aw,d,s={10工人w被分配到某个班次,(d,s) in Totherwise
e w = { 1 工人w被分配到周末,(d,s) in E 0 otherwise e_{w} = \begin{cases} 1 & \text{工人w被分配到周末,(d,s) in E} \\ 0 & \text{otherwise} \end{cases} ew={10工人w被分配到周末,(d,s) in Eotherwise
n w = { 1 工人w在工作日被需要 0 otherwise n_{w} = \begin{cases} 1 & \text{工人w在工作日被需要} \\ 0 & \text{otherwise} \end{cases} nw={10工人w在工作日被需要otherwise
模型目标
该模型的目标是尽量减少满足轮班和工作要求所需的工人总数,同时尽量满足工人对周末轮班分配的偏好。
这是通过尽量减少满足所有轮班要求所需的工人数量与分配到周末轮班的工人数量的加权和来实现的。
由此得到的目标函数是:
min ( ∑ w ∈ W n w + γ ∑ w ∈ W e w ) \quad\quad\quad\quad\min(\sum_{w \in W}n_{w}+\gamma\sum_{w \in W}e_{w}) min(∑w∈Wnw+γ∑w∈Wew)
权重是一个固定的正参数,决定在轮班计划中这两个指标的相对重要性
模型约束
- 每个白班,需要有足够的工人来满足人员配备要求
∑ w ∈ W a w , d , s ≥ R d , s ∀ ( d , s ) ∈ T \quad\quad\quad \quad\sum_{w ∈ W}a_{w,d,s} ≥ R_{d,s}\quad\quad∀ (d,s) ∈ T ∑w∈Waw,d,s≥Rd,s∀(d,s)∈T
- 每周不得为任何工人分配超过40小时的工作时间
8 ∑ d , s ∈ T a w , d , s ≤ 40 ∀ w ∈ W \quad\quad\quad\quad8\sum\limits_{d,s \in T}a_{w,d,s} ≤ 40 \quad\quad\quad ∀ w ∈ W 8d,s∈T∑aw,d,s≤40∀w∈W
- 每个工人24小时内只能分配一个班次
a w , d 1 , s 1 + a w , d 2 , s 2 + a w , d 3 , s 3 ≤ 1 ∀ w ∈ W ∀ ( ( d 1 , s 1 ) , ( d 2 , s 2 ) , ( d 3 , s 3 ) ) ∈ B \quad\quad\quad\quad a_{w,d1,s1}+a_{w,d2,s2}+a_{w,d3,s3} ≤ 1 \quad ∀ w∈ W \\\qquad\quad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\quad ∀ ((d1,s1),(d2,s2),(d3,s3)) ∈ B aw,d1,s1+aw,d2,s2+aw,d3,s3≤1∀w∈W∀((d1,s1),(d2,s2),(d3,s3))∈B
- 当工人被分配到任何一个白班 n w = 1 n_w=1 nw=1
∑ d , s ∈ T a w , d , s ≤ 21 ⋅ n w ∀ w ∈ W \quad\quad\quad\sum\limits_{d,s \in T}a_{w,d,s} ≤ 21 ⋅ n_w\quad\quad ∀ w ∈ W d,s∈T∑aw,d,s≤21⋅nw∀w∈W
- 当工人被分配到周末的任意白班 e w = 1 e_w=1 ew=1
∑ d , s ∈ E a w , d , s ≤ 6 ⋅ e w ∀ w ∈ W \quad\quad\quad\sum\limits_{d,s \in E}a_{w,d,s} ≤ 6 ⋅ e_w\quad\quad ∀w ∈ W d,s∈E∑aw,d,s≤6⋅ew∀w∈W
Pyomo实现
# 每个时间段都有足够数量的工人
mdl.required_workers = Constraint(mdl.T,expr=lambda mdl, day, shift:mdl.R[day, shift] == sum(mdl.a[worker, day, shift] for worker in mdl.W))
# 周工作时间不超过上限
mdl.weekly_hours = Constraint(mdl.W, expr=lambda mdl,worker:8 * sum(mdl.a[worker, day, shift] for day, shift in mdl.T) <= mdl.H)
# 工人每24小时只能工作一次
mdl.required_rest = Constraint(mdl.W, mdl.B,expr=lambda mdl, worker,d1, s1, d2, s2, d3, s3:mdl.a[worker, d1, s1] + mdl.a[worker, d2, s2] + mdl.a[worker, d3, s3] <= 1 )
# 一名工人是否分配班次
mdl.is_needed = Constraint(mdl.W, expr=lambda mdl,worker:sum(mdl.a[worker, day, shift] for day, shift in mdl.T) <= len(mdl.T) * mdl.n[worker] )
# 工人是否被分配到周末
mdl.is_weekend = Constraint(mdl.W, expr=lambda mdl,worker:sum(mdl.a[worker, day, shift] for day, shift in mdl.E) <= 6 * mdl.e[worker]
# 目标函数
mdl.obj = Objective(expr=sum(mdl.n[worker] + 0.1 * mdl.e[worker]for i, worker in enumerate(mdl.W)),sense=minimize)
排班方案可视化展示
工人排版表
工人01的班次表 工人02的班次表 工人03的班次表 工人04的班次表 工人 星期 班次 工人 星期 班次 工人 星期 班次 工人 星期 班次
工人01 周一 晚上 工人02 周三 早上 工人03 周二 早上 工人04 周一 下午
工人01 周二 晚上 工人02 周四 早上 工人03 周三 下午 工人04 周三 早上
工人01 周五 早上 工人02 周五 早上 工人03 周五 下午 工人04 周四 早上
工人01 周六 早上 工人02 周六 早上 工人03 周六 下午 工人04 周六 晚上
工人01 周日 早上 工人03 周日 下午 工人04 周日 晚上工人05的班次表 工人06的班次表 工人07的班次表 工人 星期 班次 工人 星期 班次 工人 星期 班次
工人05 周一 早上 工人06 周一 早上 工人07 周一 下午
工人05 周二 早上 工人06 周二 下午 工人07 周二 下午
工人05 周四 下午 工人06 周三 下午 工人07 周三 晚上
工人05 周六 下午 工人06 周四 下午 工人07 周四 晚上 工人06 周五 下午 工人07 周五 晚上 工人08的班次表 工人09的班次表 工人10的班次表无班次安排 无班次安排 无班次安排