OR-Tools带休息间隔与休假豁免的护士排班问题建模咨询
护士排班时间间隔约束实现方案
你当前使用的布尔变量建模框架完全可以满足需求,不需要更换建模方式,只需要补充预计算逻辑和对应约束即可,具体实现步骤如下:
步骤1:预结构化班次信息
先把所有班次的时间统一转换为分钟数,避免后续重复计算时间差:
# 班次信息:key=班次编号,value=(开始时间分钟, 结束时间分钟, 是否为休息班次) shift_info = { 0: (0, 0, True), 1: (480, 900, False), # 8:00-15:00 2: (900, 1320, False), # 15:00-22:00 3: (480, 1320, False), # 8:00-22:00 # 其余班次按规则补充即可 }
同时提前加好已经明确的基础约束:
# 每名员工每日仅分配1个班次 for n in all_nurses: for d in all_days: model.Add(sum(shifts[(n, d, s)] for s in all_shifts) == 1) # 员工申请休假的日期只能排休息班次 # 假设leave_days[n]是员工n申请休假的日期列表 for n in all_nurses: for d in leave_days[n]: model.Add(shifts[(n, d, 0)] == 1)
步骤2:实现间隔约束
两次排班的间隔超过24小时的情况,必然会间隔至少1个自然日,所以我们只需要覆盖两种场景即可:
场景1:相邻两天的排班约束
预计算所有相邻天排班的非法组合:也就是前一天下班到第二天上班的间隔不在12~24小时区间的班次对,直接禁止同时出现:
invalid_adjacent_pairs = [] for s_prev in [s for s in all_shifts if not shift_info[s][2]]: prev_end = shift_info[s_prev][1] for s_curr in [s for s in all_shifts if not shift_info[s][2]]: curr_start = shift_info[s_curr][0] # 相邻天的间隔 = 1天分钟数 + 本次上班时间 - 上次下班时间 gap = 1440 + curr_start - prev_end if gap < 720 or gap > 1440: # 小于12小时或大于24小时 invalid_adjacent_pairs.append((s_prev, s_curr)) # 添加相邻天非法组合禁止约束 for n in all_nurses: for d in range(len(all_days)-1): for s_prev, s_curr in invalid_adjacent_pairs: model.Add(shifts[(n, d, s_prev)] + shifts[(n, d+1, s_curr)] <= 1)
场景2:间隔1天的排班约束
如果两次排班间隔1个自然日且中间没有休假,那么间隔必然超过24小时,属于非法情况,因此约束为:如果前后两天都排了非休息班次,中间那天必须是休假:
for n in all_nurses: for d in range(len(all_days)-2): for s_prev in [s for s in all_shifts if not shift_info[s][2]]: for s_curr in [s for s in all_shifts if not shift_info[s][2]]: # 仅当d和d+2天都排了非休息班次时,要求d+1天必须是休息 model.Add(shifts[(n, d+1, 0)] == 1).OnlyEnforceIf([ shifts[(n, d, s_prev)], shifts[(n, d+2, s_curr)] ])
以上逻辑已经天然支持约束2:只要两次排班中间有休假,间隔限制自动不生效。
可选替代建模方式
如果你觉得布尔变量的约束编写太零散,可以改用单整数变量代表每日班次的建模方式:每个shift_n%id%i变量的取值范围是0到最大班次编号,本质和布尔变量建模等价,约束写法会更简洁,求解效率没有明显差异。
内容的提问来源于stack exchange,提问作者Álvaro Ruiz
相关产品推荐
相关产品推荐

