You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 03:06:38