docplex.cp.model求解速度慢于穷举搜索,如何优化CPLEX求解效率?
优化方案
你当前遇到的性能差异核心原因有两个:
- 你调用的是
docplex.cp对应的CP Optimizer约束规划求解器,不是处理线性整数规划的传统CPLEX MILP求解器,这类通用求解器本身会有分支定界、剪枝的额外开销,适合变量规模大、穷举无法覆盖的场景。你当前的测试场景S=10、k=2,总合法组合只有C(10,1)+C(10,2)=55种,穷举本身就是最优解法,求解器的通用逻辑反而会被冗余的表达式处理拖慢。 - 你当前的CPO模型写法存在明显的性能问题:目标函数展开后包含5000*10=50000个运算节点,表达式树非常庞大,求解器解析和处理的成本极高。
具体优化方法
方案1:直接使用穷举(最适合当前场景)
如果你的业务场景里S永远不超过20、k<=3,总组合数在1e4以内,完全不需要调用商用求解器,直接用你写的numpy向量化穷举代码即可,性能远高于通用求解器。方案2:简化CPO模型结构
利用当前组合数量少的特点,重新建模减少表达式冗余:
先预计算所有合法组合对应的总收益,再给每个组合设置一个二进制变量,约束只能选一个组合,目标直接取选中组合对应的收益即可,修改后代码如下:
import numpy as np from docplex.cp.model import CpoModel N = 5000 S = 10 k = 2 u_i = np.random.rand(N)[:,np.newaxis] u_ij = np.random.rand(N*S).reshape(N, S) beta = np.random.rand(N)[:,np.newaxis] # 预计算所有合法组合的总收益 from itertools import combinations valid_combs = [] comb_rev = [] for K_i in range(1, k+1): for comb in combinations(range(S), K_i): I = np.zeros(S) I[list(comb)] = 1 u_j = u_ij @ I total_rev = np.sum(beta / (1 + u_i / u_j[:, np.newaxis])) valid_combs.append(comb) comb_rev.append(total_rev) # 新建简化后的模型 m = CpoModel(name = 'simplified_model') # 每个组合对应一个二进制变量 select = m.binary_var_list(len(valid_combs)) # 约束只能选一个组合 m.add_constraint(m.sum(select) == 1) # 目标直接是选中组合的收益 m.maximize(m.sum(select[i] * comb_rev[i] for i in range(len(valid_combs)))) sol = m.solve(agent='local') sol.print_solution() # 输出选中的组合 for i in range(len(valid_combs)): if sol[select[i]] == 1: print('最优组合:', valid_combs[i])
修改后模型的表达式规模从5万级降到了几十级,求解时间会降到1秒以内。
- 方案3:调整CPO求解参数
如果不想修改模型结构,可以在调用solve时传入参数降低求解开销:
sol = m.solve( agent='local', params={ 'OptimalityTolerance': 1e-3, # 放宽最优性 tolerance,不需要到1e-5的精度 'Workers': 4, # 开启多线程 'SearchType': 'DepthFirst', # 换用深度优先搜索,适合小规模问题 'TimeLimit': 10 # 加超时限制避免无意义等待 } )
- 方案4:换用CPLEX MILP求解器并线性化目标
如果后续S规模扩大到几十到上百,穷举不可行,可以把目标函数中的非线性除法项用大M法线性化,再调用docplex.mp的MILP求解器,性能会比CPO高一个数量级。
内容的提问来源于stack exchange,提问作者S.Perera
相关产品推荐
相关产品推荐

