使用OR-Tools CP-SAT加速大规模指派问题求解


使用OR-Tools CP-SAT加速大规模指派问题求解

本文旨在解决使用`ortools.linear_solver`处理大规模指派问题时遇到的性能瓶颈,特别是当问题规模(n)超过40-50时。针对包含复杂定制约束(如特定id分配、id分组及id和限制)以及最小化最高与最低成本差值的目标函数,我们推荐并详细演示如何通过迁移至or-tools的cp-sat求解器来显著提升求解速度,同时提供浮点系数处理和性能调优的实践指导。

指派问题是运筹学中的经典问题,旨在将一组工作者分配给一组任务,以优化特定目标。当问题规模较小或约束相对简单时,ortools.linear_solver(特别是结合SCIP后端)是一个强大且灵活的工具。然而,对于大规模问题(例如,N超过40-50),尤其当问题包含整数变量、复杂逻辑约束以及如最小化最高与最低成本差异等非线性目标时,linear_solver的求解时间可能会急剧增加,变得不切实际。

问题描述与原始方法分析

假设我们面临一个复杂的指派问题,其核心要求包括:

  1. 目标函数:最小化所有工作者中被分配任务的最高成本与最低成本之间的差值。
  2. 工作者特性:每位工作者拥有一个特定ID(例如“A”、“B”、“C”、“D”)。
  3. 定制约束
    • 某些任务只能分配给具有特定ID的工作者。
    • 某些任务集合必须分配给具有相同ID的工作者。
    • 某些任务集合所分配工作者的ID值之和必须限制在特定范围内(例如,将“A”映射为0,“B”映射为1等)。

原始实现中,用户采用了pywraplp.Solver.CreateSolver("SCIP")来构建模型。SCIP是一个强大的混合整数规划(MIP)求解器,能够处理整数和连续变量。然而,对于纯整数模型或高度组合性的问题,SCIP可能不如专门的约束规划(CP)求解器高效。当问题规模增大时,SCIP在探索搜索空间时可能需要更多时间。用户提出的关于调整参数(如PRESOLVE_ON)或提供初始解的疑问,虽然在某些情况下可能有所帮助,但对于这种类型的性能瓶颈,通常需要考虑更换更适合的求解器。

推荐解决方案:使用OR-Tools CP-SAT求解器

对于上述问题,由于其本质上是一个纯整数模型(尽管成本是浮点数,但分配决策是0-1整数),并且包含大量的离散约束和组合特性,OR-Tools的CP-SAT求解器是更优的选择。CP-SAT是Google开发的一个强大的约束规划和SAT求解器,在处理整数变量、布尔变量和各种逻辑约束方面表现卓越,尤其擅长处理大规模的组合优化问题。

CP-SAT的优势在于:

  • 专为整数和布尔变量优化:它内部使用了多种先进的启发式算法和传播技术,能够高效地剪枝搜索空间。
  • 处理复杂约束:CP-SAT原生支持各种复杂的逻辑约束,如AddMaxEquality、AddMinEquality等,这些在传统线性规划中可能需要引入大量辅助变量和MIP技巧才能表达。
  • 性能优异:在许多整数规划和约束规划基准测试中,CP-SAT都展现出领先的性能。

将模型迁移到CP-SAT

以下是将原始linear_solver模型转换为CP-SAT模型的详细步骤和示例代码。

1. 导入必要的库

from ortools.sat.python import cp_model
import numpy as np

2. 定义问题参数和数据

与原代码相同,定义工作者、任务数量、成本矩阵和工作者ID。

Animate AI Animate AI

Animate AI是个一站式AI动画故事视频生成工具

Animate AI 234 查看详情 Animate AI
# number of workers and tasks
N = 40 # Increased to N=70 for demonstration of scalability

# cost table for each worker-task pairs
np.random.seed(0)
costs = np.random.rand(N,N)*100

# workers IDs    
workers_id = (np.random.rand(N)*4).astype(np.uint32)
id_2_idsrt_dict = {0: 'A', 1: 'B', 2: 'C', 3: 'D'}        
workers_id_str = [id_2_idsrt_dict[val] for val in workers_id]

idsrt_2_id_dict = {idstr: id for id, idstr in id_2_idsrt_dict.items()}

num_workers = len(costs)
num_tasks = len(costs[0])

3. 处理浮点成本:整数化

CP-SAT主要处理整数。原始问题中的costs是浮点数。为了在CP-SAT中使用,我们需要将其转换为整数。最常见的方法是乘以一个足够大的因子(例如100或1000)并取整,从而保留足够的精度。

# Scale costs to integers to use with CP-SAT
# Multiplying by 100 to keep two decimal places precision
scaling_factor = 100
scaled_costs = (costs * scaling_factor).astype(int)

# Determine bounds for scaled costs
min_scaled_cost_val = int(np.min(scaled_costs))
max_scaled_cost_val = int(np.max(scaled_costs))

# Max possible cost for a single worker (since each worker takes one task)
# This is also the upper bound for max_cost and lower bound for min_cost
max_possible_worker_cost = max_scaled_cost_val
min_possible_worker_cost = min_scaled_cost_val

4. 创建CP-SAT模型和变量

使用cp_model.CpModel()创建模型。

  • x[i, j]:布尔变量,表示工作者i是否分配给任务j。
  • tasks_ids[j]:整数变量,表示任务j分配的工作者的ID。其取值范围是0到3(对应'A'到'D')。
# Create the CP-SAT model
model = cp_model.CpModel()

# Variables
# x[i, j] is a 0-1 variable, which will be 1 if worker i is assigned to task j.
x = {}
for i in range(num_workers):   
  for j in range(num_tasks):
     x[i, j] = model.NewBoolVar(f"x_{i}_{j}")

# tasks_ids[j] is an integer variable containing each task's assigned worker id.
# Worker IDs range from 0 to 3.
tasks_ids = []
for j in range(num_tasks):
   # The ID for a task will be the ID of the worker assigned to it.
   # We define its range from 0 to 3 (corresponding to 'A' to 'D').
   tasks_ids.append( model.NewIntVar(0, 3, f"task_id_{j}") )

   # Link task_id to the worker_id of the assigned worker
   # tasks_ids[j] == sum(workers_id[i] * x[i, j] for i in range(num_workers))
   model.Add(tasks_ids[j] == sum(workers_id[i] * x[i, j] for i in range(num_workers)))

5. 添加约束

将原始问题中的所有约束迁移到CP-SAT模型。

# Constraint: Each worker is assigned to exactly one task.
for i in range(num_workers):
   model.Add(sum(x[i, j] for j in range(num_tasks)) == 1)

# Constraint: Each task is assigned to exactly one worker.
for j in range(num_tasks):
   model.Add(sum(x[i, j] for i in range(num_workers)) == 1)   

# Constraint: Task 1 can be assigned only with workers that h*e the id "A"     
model.Add(tasks_ids[1] == idsrt_2_id_dict["A"])  

# Constraint: Tasks 2,4,6 must assigned with workers of the same id
model.Add(tasks_ids[2] == tasks_ids[4]) 
model.Add(tasks_ids[2] == tasks_ids[6]) 

# Constraint: Tasks 10,11,12 must assigned with workers of the same id
model.Add(tasks_ids[10] == tasks_ids[11]) 
model.Add(tasks_ids[11] == tasks_ids[12]) 

# Constraint: Tasks 1,2,3 sum of ids <= 4
model.Add((tasks_ids[1] + tasks_ids[2] + tasks_ids[3]) <= 4) 

# Constraint: Tasks 4,5,6 sum of ids <= 4
model.Add((tasks_ids[4] + tasks_ids[5] + tasks_ids[6]) <= 4) 

# Constraint: Tasks 7,8,9 sum of ids <= 3
model.Add((tasks_ids[7] + tasks_ids[8] + tasks_ids[9]) <= 3) 

6. 定义目标函数:最小化成本差异

这是CP-SAT展现其优势的关键部分。为了最小化最高成本与最低成本的差值,我们需要引入辅助变量并使用AddMaxEquality和AddMinEquality。

# Objective: minimize the difference of assignment higher cost worker and lower cost worker

# List of scaled costs for each worker's assignment
assignment_workers_scaled_costs_vars = []
for i in range(num_workers):            
   # Each worker's cost is the sum of scaled_costs[i][j] * x[i,j] for their assigned task.
   # The bounds for this variable are min_possible_worker_cost to max_possible_worker_cost.
   worker_cost_var = model.NewIntVar(min_possible_worker_cost, max_possible_worker_cost, f'worker_scaled_cost_{i}')
   model.Add(worker_cost_var == sum(scaled_costs[i][j] * x[i, j] for j in range(num_tasks)))
   assignment_workers_scaled_costs_vars.append(worker_cost_var)

# Additional variables for max and min costs
max_cost_var = model.NewIntVar(min_possible_worker_cost, max_possible_worker_cost, 'max_scaled_cost')        
min_cost_var = model.NewIntVar(min_possible_worker_cost, max_possible_worker_cost, 'min_scaled_cost')

# Constraints to update max and min costs using CP-SAT's dedicated functions
model.AddMaxEquality(max_cost_var, assignment_workers_scaled_costs_vars)
model.AddMinEquality(min_cost_var, assignment_workers_scaled_costs_vars)

# Minimize the difference between max and min costs
model.Minimize(max_cost_var - min_cost_var)

7. 求解模型

创建cp_model.CpSolver()实例并调用Solve()方法。

# Create a solver and solve the model.
solver = cp_model.CpSolver()

# Optional: Configure solver parameters for tuning
# solver.parameters.log_search_progress = True # Enable logging to see solver progress
# solver.parameters.num_workers = 8 # Use multiple cores for parallel search (if applicable)
# solver.parameters.max_time_in_seconds = 60.0 # Set a time limit

print(f"Solving with CP-SAT solver version: {solver.CpSolverVersion()}")
status = solver.Solve(model)

# Print solution.
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
   # The objective value is scaled, so divide by scaling_factor to get original units
   objective_diff = solver.ObjectiveValue() / scaling_factor
   print(f"Difference (min_max_cost) = {objective_diff:.2f}\n")
   print(f"Max scaled cost: {solver.Value(max_cost_var) / scaling_factor:.2f}")
   print(f"Min scaled cost: {solver.Value(min_cost_var) / scaling_factor:.2f}")

   for i in range(num_workers):
      for j in range(num_tasks):
         if solver.Value(x[i, j]) > 0.5: # If x[i,j] is 1
            print(f"Worker {i} ({workers_id_str[i]}) assigned to task {j}." + 
                  f" Original Cost: {costs[i][j]:.2f}, Scaled Cost: {scaled_costs[i][j]}")
else:
   print("No solution found.")
   if status == cp_model.INFEASIBLE:
       print("The problem is infeasible.")
   elif status == cp_model.MODEL_INVALID:
       print("The model is invalid.")
   else:
       print(f"Solver status: {solver.StatusName(status)}")

CP-SAT性能调优与注意事项

  1. 浮点数处理:如上所示,对于CP-SAT,最佳实践是将所有浮点系数(如成本)手动缩放为整数。虽然CP-SAT内部会尝试自动缩放,但手动缩放可以提供更好的控制和可预测性。缩放因子应根据所需精度和数值范围来选择,避免溢出。
  2. 参数设置:CP-SAT提供了丰富的参数来调优求解过程。可以通过solver.parameters对象进行设置。
    • `solver.parameters

以上就是使用OR-Tools CP-SAT加速大规模指派问题求解的详细内容,更多请关注其它相关文章!


# go  # 美业关键词排名  # 网站建设如何提升权重  # 酒吧营销与推广的区别  # 龙泉区文明网站建设公示  # 定制网站建设官网  # 猎眼seo  # 苏州网站建设及优化  # 将其  # 是个  # 线性规划  # 这是  # 转换为  # 浮点数  # 布尔  # 是一个  # 浮点  # elif  # cos  # 性能瓶颈  # google  # win  # ai  # 后端  # 工具  # app  # python  # 商丘网站建设广告设计  # 银川外贸网站建设  # 网站内部优化做什么的 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 动漫岛在线动漫网 动漫岛动漫在线观看官方入口  《兴业银行》注册登录方法  《杖剑传说》食谱大全  个人所得税办理入口 个人所得税综合所得年度汇算入口  智慧职教mooc平台登录网址 智慧职教mooc官网直达  抖音火山版注销账号抖音会注销吗 抖音火山版与抖音账号注销关系  以下哪一个是适应长期护理制度发展而设立的新职业  iPhone14无法连接蓝牙设备如何解决  MySQL多重JOIN技巧:高效关联同一表获取多角色信息  win11怎么启用或禁用休眠 Win11 powercfg命令管理休眠文件【技巧】  J*aScript与HTML元素交互:图片点击事件与链接处理教程  精通VS Code多光标编辑以实现闪电般快速的修改  电脑开不了机怎么办 电脑无法开机的解决方法  123网页端官方登录页 123邮箱网页版即时通讯服务  iSpring三分屏制作教程  Sublime怎么配置YAML文件格式化_Sublime YAML Formatter插件教程  荣耀magicv5怎么上手测评  《飞猪旅行》购买汽车票方法  《图怪兽》退出登录方法  c++中的const关键字用法大全_c++ const正确使用指南  网易云音乐闹钟铃声设置教程  优化2xN网格最大路径和的动态规划算法实践  QQ网页版入口导航 QQ网页版在线访问通道  在XML中嵌入二进制数据(如图片)的最佳实践是什么? Base64编码与解析注意事项  从J*a应用程序中导出MySQL表数据的技术指南  Yandex浏览器官方入口_Yandex搜索引擎中文版  多闪电脑版下载_多闪PC端模拟器使用  悟空浏览器网页版链接 悟空浏览器网页版最新有效地址  抖音号显示企业机构号是什么意思?企业机构号申请条件是什么?  QQ邮箱注册地址 免费获取QQ邮箱账号  React应用中Commerce.js数据加载与状态管理最佳实践  使用jQuery精确检测除指定元素外任意位置的点击事件  RxJS中如何高效地在一个函数内处理和合并多个数据集合  《U校园》学生登录入口2025  J*aScript:从子元素中批量移除特定CSS类  12306售票时间最新规定 | 网上订票和车站窗口时间一样吗  PHP 4 函数中引用参数的默认值限制与解决方案  在Peewee中处理PostgreSQL记录重复:一站式数据摄取教程  《淘宝联盟》推广自己的店铺方法  WPS长文档分栏排版不乱方法_WPS分栏+分节符报纸排版教程  快手缓存清理方法  泰拉瑞亚网页版在线登录入口 泰拉瑞亚官方正版入口  Yandex俄罗斯搜索引擎官网入口 Yandex网页端直接访问  J*aScript 数值去小数位处理:多种方法与实践  咸鱼怎么设置仅粉丝可见的动态_咸鱼动态粉丝可见设置方法  抖音号已注销怎么解绑企业认证?不解绑企业认证会怎样?  win11自带录屏文件保存在哪里 Win11 Game Bar录制视频默认路径【分享】  喜茶GO更换登录账号方法  晓晓优选app支付宝绑定方法  《procreate》绘制渐变效果教程 

 2025-11-16

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.