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

如何在OR-Tools中定义目标函数以最小化指派问题的成本极值差?

问题:最小化指派问题的成本极值差(OR-Tools实现)

现有3名工人、4项任务,成本矩阵如下:

costs = [
        [90, 80, 75, 70],
        [35, 85, 55, 65],
        [125, 95, 90, 95],
    ]

要求为每名工人分配一项任务,无需最小化总成本,而是最小化成本的极值差(即让工人的任务成本尽可能接近)。例如工人0→任务0、工人1→任务1、工人2→任务2时,极值差为5,是最优结果。

尝试的代码与错误

使用OR-Tools的MIP求解器实现时,运行以下代码报错:

from ortools.linear_solver import pywraplp

def main():
    # Data
    costs = [
        [90, 80, 75, 70],
        [35, 85, 55, 65],
        [125, 95, 90, 95],
    ]
    num_workers = len(costs)
    num_tasks = len(costs[0])

    # Solver
    solver = pywraplp.Solver.CreateSolver("SCIP")

    if not solver:
        return

    # Variables
    x = {}
    for i in range(num_workers):
        for j in range(num_tasks):
            x[i, j] = solver.IntVar(0, 1, "")

    # Constraints
    for i in range(num_workers):
        solver.Add(solver.Sum([x[i, j] for j in range(num_tasks)]) == 1)

    for j in range(num_tasks):
        solver.Add(solver.Sum([x[i, j] for i in range(num_workers)]) <= 1)

    # Objective
    objective_terms = []
    for i in range(num_workers):
        for j in range(num_tasks):
            objective_terms.append(costs[i][j] * x[i, j])

    objective_cost = max(objective_terms)-min(objective_terms)
    solver.Minimize(objective_cost)

    # Solve
    print(f"Solving with {solver.SolverVersion()}")
    status = solver.Solve()

    # Print solution.
    if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
        print(f"Total cost = {solver.Objective().Value()}\n")
        for i in range(num_workers):
            for j in range(num_tasks):
                if x[i, j].solution_value() > 0.5:
                    print(f"Worker {i} assigned to task {j}." + f" Cost: {costs[i][j]}")
    else:
        print("No solution found.")


if __name__ == "__main__":
    main()

报错信息:

Operators "<" and ">" not supported with the linear solver

错误核心:objective_terms中的元素是OR-Tools的变量对象,无法直接用Python内置的max()/min()函数处理,线性求解器不支持这种非线性表达式。


解决方案:OR-Tools两种实现方式

这类问题完全可以用OR-Tools解决,以下是两种可行方案:

方案一:MIP求解器线性化目标函数

通过引入辅助变量max_cost和min_cost,将极值差的非线性目标转化为线性约束:

from ortools.linear_solver import pywraplp

def main():
    costs = [
        [90, 80, 75, 70],
        [35, 85, 55, 65],
        [125, 95, 90, 95],
    ]
    num_workers = len(costs)
    num_tasks = len(costs[0])

    solver = pywraplp.Solver.CreateSolver("SCIP")
    if not solver:
        return

    # 0-1分配变量
    x = {}
    for i in range(num_workers):
        for j in range(num_tasks):
            x[i, j] = solver.IntVar(0, 1, f"x_{i}_{j}")

    # 每个工人分配恰好一项任务
    for i in range(num_workers):
        solver.Add(solver.Sum([x[i, j] for j in range(num_tasks)]) == 1)

    # 每项任务最多分配给一个工人
    for j in range(num_tasks):
        solver.Add(solver.Sum([x[i, j] for i in range(num_workers)]) <= 1)

    # 定义每个工人的实际成本表达式
    worker_cost = []
    for i in range(num_workers):
        cost_expr = solver.Sum([costs[i][j] * x[i, j] for j in range(num_tasks)])
        worker_cost.append(cost_expr)

    # 引入最大、最小成本变量
    max_cost = solver.NumVar(0, solver.infinity(), "max_cost")
    min_cost = solver.NumVar(0, solver.infinity(), "min_cost")

    # 约束:max_cost >= 所有工人的成本
    for cost in worker_cost:
        solver.Add(max_cost >= cost)

    # 约束:min_cost <= 所有工人的成本
    for cost in worker_cost:
        solver.Add(min_cost <= cost)

    # 目标:最小化极值差
    solver.Minimize(max_cost - min_cost)

    # 求解
    status = solver.Solve()

    if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
        print(f"最小极值差 = {solver.Objective().Value()}\n")
        for i in range(num_workers):
            for j in range(num_tasks):
                if x[i, j].solution_value() > 0.5:
                    print(f"工人 {i} 分配任务 {j},成本:{costs[i][j]}")
    else:
        print("未找到可行解。")

if __name__ == "__main__":
    main()

方案二:使用CP-SAT求解器

CP-SAT原生支持极值运算,实现更直观:

from ortools.sat.python import cp_model

def main():
    costs = [
        [90, 80, 75, 70],
        [35, 85, 55, 65],
        [125, 95, 90, 95],
    ]
    num_workers = len(costs)
    num_tasks = len(costs[0])

    model = cp_model.CpModel()

    # 0-1分配变量
    x = {}
    for i in range(num_workers):
        for j in range(num_tasks):
            x[i, j] = model.NewBoolVar(f"x_{i}_{j}")

    # 每个工人分配恰好一项任务
    for i in range(num_workers):
        model.AddExactlyOne(x[i, j] for j in range(num_tasks))

    # 每项任务最多分配给一个工人
    for j in range(num_tasks):
        model.AddAtMostOne(x[i, j] for i in range(num_workers))

    # 定义每个工人的成本
    worker_cost = []
    for i in range(num_workers):
        cost_vars = []
        for j in range(num_tasks):
            cost_vars.append(cp_model.LinearExpr.Term(x[i, j], costs[i][j]))
        total_cost = cp_model.LinearExpr.Sum(cost_vars)
        worker_cost.append(total_cost)

    # 计算最大和最小成本
    max_cost = model.NewIntVar(0, max(max(row) for row in costs), "max_cost")
    min_cost = model.NewIntVar(0, max(max(row) for row in costs), "min_cost")

    model.AddMaxEquality(max_cost, worker_cost)
    model.AddMinEquality(min_cost, worker_cost)

    # 目标:最小化极值差
    model.Minimize(max_cost - min_cost)

    # 求解
    solver = cp_model.CpSolver()
    status = solver.Solve(model)

    if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
        print(f"最小极值差 = {solver.ObjectiveValue()}\n")
        for i in range(num_workers):
            for j in range(num_tasks):
                if solver.Value(x[i, j]):
                    print(f"工人 {i} 分配任务 {j},成本:{costs[i][j]}")
    else:
        print("未找到可行解。")

if __name__ == "__main__":
    main()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 23:33:09