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

使用OR-Tools CP-SAT最小化司机总数的约束问题排查

问题:OR-Tools SAT求解器司机排班约束错误,多班次场景结果异常

核心需求

  • 目标:最小化司机数量
  • 约束条件:
    • 每个班次必须分配给至少一名司机
    • 同一司机同一时间只能承接一个班次(无时间重叠)
    • 司机承接的连续班次需满足:前一班结束时间 ≤ 下一班开始时间

现有问题

3-4个班次时调整约束后结果正确,但5-6个班次时结果不符合预期或显示不可行,怀疑约束语句model.Add((1 - tasks_assignment[driver, task]) >= tasks_assignment[driver,task2]).OnlyEnforceIf(task_overlap[driver, task])存在逻辑错误。

问题分析

  1. 约束逻辑完全反转:你的task_overlap定义是「0=班次无法连续承接,1=可连续承接」,但原约束的逻辑是:当task_overlap[driver,task]为真(可连续)时,强制1 - tasks_assignment[driver,task] >= tasks_assignment[driver,task2]——也就是如果司机接了task,就不能接task2,这和你想要的连续承接逻辑完全相反。
  2. 兼容性矩阵维度错误:班次能否连续承接是由时间决定的,和司机无关,task_overlap应该是二维矩阵task_overlap[task1, task2],而非绑定司机的三维矩阵。

正确解决方案

1. 预生成班次兼容性矩阵

先基于班次时间生成can_assign_together二维矩阵,明确哪些班次对可以分配给同一司机:

num_tasks = 6
# 假设tasks是存储班次信息的列表,每个元素格式为[..., start_time, end_time]
can_assign_together = [[0]*num_tasks for _ in range(num_tasks)]

for task1 in range(num_tasks):
    for task2 in range(num_tasks):
        if task1 != task2:
            # 判断task1结束时间 ≤ task2开始时间(确保可连续承接)
            if tasks[task1][4] <= tasks[task2][3]:
                can_assign_together[task1][task2] = 1
            else:
                can_assign_together[task1][task2] = 0

2. 修正同一司机的班次约束

对所有不兼容的班次对,约束同一司机不能同时承接:

for driver in range(num_drivers):
    for task1 in range(num_tasks):
        for task2 in range(task1+1, num_tasks):
            # 如果两个班次既不能连续承接,也不能同时承接(时间重叠)
            if can_assign_together[task1][task2] == 0 and can_assign_together[task2][task1] == 0:
                model.Add(tasks_assignment[driver, task1] + tasks_assignment[driver, task2] <= 1)

3. 补充必要约束与目标函数

  • 确保每个班次都被分配:
for task in range(num_tasks):
    model.Add(sum(tasks_assignment[driver, task] for driver in range(num_drivers)) == 1)
  • 定义司机使用状态,最小化司机数量:
driver_used = [model.NewBoolVar(f'driver_used_{d}') for d in range(num_drivers)]
for driver in range(num_drivers):
    # 司机有排班则标记为已使用
    model.Add(sum(tasks_assignment[driver, task] for task in range(num_tasks)) >= 1).OnlyEnforceIf(driver_used[driver])
    # 司机无排班则标记为未使用
    model.Add(sum(tasks_assignment[driver, task] for task in range(num_tasks)) == 0).OnlyEnforceIf(driver_used[driver].Not())

# 目标:最小化使用的司机总数
model.Minimize(sum(driver_used))

原约束仅部分场景生效的原因

原约束逻辑反转,且错误绑定司机与班次兼容性,小数量班次时解空间小,偶然避开了冲突;但班次数量增加后,错误约束会排除大量可行解,或允许不符合时间要求的排班,导致结果异常。

内容的提问来源于stack exchange,提问作者D5015

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:30:03