Google OR Tools排班问题求助:周末班次与周五一致约束失效
排班系统约束问题排查(Google OR Tools + SCIP)
我用Google OR Tools搭配SCIP求解器开发排班系统,核心需求是周末班次需与当周周五保持一致(比如周五排早班,周六、周日也必须排早班)。针对前5周场景编写代码后,发现第13天的结果不符合预期。
我的约束要求是:将[前一日早班, 前一日晚班, 当日早班, 当日晚班]视为4位二进制数,可行数值集合应为{0,4,5,8,10},但添加所有约束后,求解结果未满足该要求。
代码实现
from ortools.linear_solver import pywraplp solver = pywraplp.Solver.CreateSolver('SCIP') emp = 5 tws = {} for eind in range(emp): tws.update({eind: []}) for week in range(5): for days in range(1,8): early = [] late = [] prev_day = (7*(week%2) + days) - 2 curr_day = (7*(week%2) + days) - 1 for i in range(emp): early.append(solver.IntVar(0,1,'es%d'%i)) late.append(solver.IntVar(0,1,'ls%d'%i)) if days < 6: if days <= 4: solver.Add(solver.Sum(early) <= (emp//2)+1) solver.Add(solver.Sum(early) >= emp//2) solver.Add(solver.Sum(late) <= emp//2) solver.Add(solver.Sum(late) >= (emp//2)-1) if days == 5: solver.Add(solver.Sum(early) == (emp//2)+1) solver.Add(solver.Sum(late) == emp//2) for i in range(emp): solver.Add(early[i]+late[i] <= 1) if days == 1: if week == 0: status = solver.Solve() for sch in range(emp): tws[sch].append([early[sch], late[sch]]) elif week > 0: status = solver.Solve() for sch in range(emp): tws[sch].append([early[sch], late[sch]]) elif days > 1: status = solver.Solve() for sch in range(emp): tws[sch].append([early[sch], late[sch]]) if days >= 6: solver.Add(solver.Sum(early) <= 1) solver.Add(solver.Sum(early) >= 1) solver.Add(solver.Sum(late) <= 1) solver.Add(solver.Sum(late) >= 1) for e in range(emp): solver.Add(early[i] + late[i] <= 1) for i in range(emp): if days == 6: solver.Add(tws[i][prev_day][0] + tws[i][prev_day][1] + early[i] + late[i] <= 3) solver.Add((tws[i][prev_day][0] + tws[i][prev_day][1]) - (early[i] + late[i]) >= 0) solver.Add((tws[i][prev_day][0] + tws[i][prev_day][1]) - (early[i] + late[i]) <= 1) solver.Add((tws[i][prev_day][0] - tws[i][prev_day][1]) - (early[i] - late[i]) >= -1) solver.Add((tws[i][prev_day][0] - tws[i][prev_day][1]) - (early[i] - late[i]) <= 1) solver.Add((tws[i][prev_day][0] + tws[i][prev_day][1])- early[i] >= -1) if days == 7: solver.Add(tws[i][prev_day-1][0] + tws[i][prev_day][0] + early[i] <= 3) solver.Add(tws[i][prev_day-1][0] >= tws[i][prev_day][0]) solver.Add(tws[i][prev_day-1][0] >= early[i]) solver.Add(tws[i][prev_day-1][0] + late[i] <= 1) solver.Add(tws[i][prev_day-1][1] + tws[i][prev_day][1] + late[i] <= 3) solver.Add(tws[i][prev_day-1][1] >= late[i]) solver.Add(tws[i][prev_day-1][1] >= tws[i][prev_day][1]) solver.Add(tws[i][prev_day-1][1] + early[i] <= 1) solver.Solve() for sch in range(emp): tws[sch].append([early[sch], late[sch]]) print(f"days: {(7*week) + days}") for es in early: print(int(es.solution_value()), end = "\t") print("\n") for ls in late: print(int(ls.solution_value()), end = "\t") print("\n")
运行输出
days: 5 1 1 1 0 0 0 0 0 1 1 days: 6 1 0 0 0 0 0 0 0 0 1 days: 12 0 0 1 1 1 1 1 0 0 0 days: 13 1 0 0 0 0 0 1 0 0 0
约束说明
(Previous day early + previous day late) + (current day early + current day late) <= 3 (Previous day early + previous day late) - (current day early + current day late) >= 0 Feasible result - {0,4,5,6,8,9,10} (Assumption - sum of bits) (Previous day early - previous day late) - (current day early - current day late) >= -1 (Previous day early - previous day late) - (current day early - current day late) >= 1 Feasible Result - {0,4,5,6,7,8,10} (Assumption - sum of bits) (Previous day early + previous day late) - (current day early) >= -1 (Previous day early + previous day late) - (current day early) >= 1 Feasible Result - {0,1,2,3,4,5,8,9,10} (Assumption - sum of bits)
问题排查关键点
1. 核心需求约束未直接实现
当前代码用复杂的差值约束试图实现周末与周五班次一致,但逻辑绕弯且未命中核心需求。正确的约束应该直接绑定周末与周五的班次:
- 对每个员工,周六的早班必须等于周五的早班,晚班必须等于周五的晚班
- 周日同理,直接绑定周五的班次,而非依赖周六的结果
2. 变量与求解时机错误
- 代码每天循环创建新的
early/late变量,导致变量重复添加,约束逻辑混乱。应一次性创建所有日期的班次变量,按日期索引存储。 - 多次调用
solver.Solve(),导致求解器在约束未完全添加时就给出结果,后续约束无法生效。需在所有约束添加完成后,仅调用一次求解。
3. 循环变量索引错误
在周末约束的循环中,for e in range(emp):块内误用了外层循环的i变量,应为early[e] + late[e] <=1,否则会导致索引越界或约束绑定错误。
4. 日期索引计算错误
prev_day的计算逻辑错误,未正确指向当周周五的索引,导致约束绑定到了错误的日期。
修正建议示例
针对周末班次的核心约束,可修改为:
# 处理周六(days=6) if days == 6: # 获取当周周五的班次索引(假设tws按日期顺序存储,每周7天,从第0天开始) friday_idx = week *7 +4 for i in range(emp): friday_early = tws[i][friday_idx][0] friday_late = tws[i][friday_idx][1] # 周六班次必须等于周五 solver.Add(early[i] == friday_early) solver.Add(late[i] == friday_late) # 处理周日(days=7) if days ==7: friday_idx = week *7 +4 for i in range(emp): friday_early = tws[i][friday_idx][0] friday_late = tws[i][friday_idx][1] solver.Add(early[i] == friday_early) solver.Add(late[i] == friday_late)
内容的提问来源于stack exchange,提问作者Ipvikukiepki-KQS
相关产品推荐
相关产品推荐

