Google OR-Tools变量动态系数与取值获取问题及替代方案咨询
问题
我正在使用Google OR-Tools进行预算分配以实现收益最大化。在下方示例中,mon、tues为周一、周二的预算分配变量(占总预算的比例)。
简言之,我能否为变量设置动态系数(系数取决于求解器在寻找最优值时尝试的变量取值),而非静态系数?当在求解器的函数中访问mon、tues变量时,如何获取求解器尝试的变量数值而非变量名?
由于收益并非固定值,它取决于预算分配,因此mon、tues的取值变化会导致收益变化。我希望根据求解器尝试的mon、tues取值更新收益值,而非使用与变量关联的静态收益值(或静态系数)。尝试通过如下OR-Tools示例实现时,因mon、tues仅为变量类型而非数值,运行报错。
请问是否可在OR-Tools中实现该需求?若不可,是否需要更换其他框架/库?
def get_revenue(x): # dummy, in reality this will be tree based model return np.random.rand(1)*1000 def revenue (mon, tues): print("Values of Monday, Tuesday are : ", mon, tues) mon_revenue = get_revenue(mon) tues_revenue = get_revenue(tues) return mon*mon_revenue + tues*tuesday_revenue solver = pywraplp.Solver('Maximize', pywraplp.Solver.GLOP_LINEAR_PROGRAMMING) mon = solver.NumVar(0.2, 0.8, 'monday') tues = solver.NumVar(0.2, 0.8, 'Tuesday') solver.Add(mon + tues == 1) solver.Maximize(revenue(mon,tues)) status = solver.Solve()
解答
核心结论
OR-Tools的线性规划求解器(如GLOP)无法处理你需求中的动态系数目标函数,因为它仅支持线性/凸非线性的显式数学表达式,系数必须是静态值,不能依赖变量自身的取值。
报错原因分析
你当前的代码报错,本质是因为mon、tues是OR-Tools的NumVar对象,并非数值类型,无法直接传入get_revenue这类需要数值输入的函数。OR-Tools的求解逻辑是基于数学表达式构建求解模型,而非执行Python代码动态计算系数。
可行方案
根据你的收益函数类型,分两种情况选择工具:
1. 收益函数可转化为显式非线性表达式
如果你的树模型可以转化为二次、凸等可解析的非线性函数,可以使用OR-Tools的SCIP求解器(支持非线性规划),通过构建显式的非线性目标函数来实现。示例代码:
from ortools.linear_solver import pywraplp def revenue_expr(mon, tues): # 示例:将收益转化为可解析的非线性表达式 return mon*mon + tues*tues solver = pywraplp.Solver('Maximize', pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING) mon = solver.NumVar(0.2, 0.8, 'monday') tues = solver.NumVar(0.2, 0.8, 'tuesday') solver.Add(mon + tues == 1) # 构建非线性目标函数 solver.Maximize(revenue_expr(mon, tues)) status = solver.Solve() if status == pywraplp.Solver.OPTIMAL: print(f"最优分配:周一{mon.solution_value():.4f},周二{tues.solution_value():.4f}")
2. 收益函数是黑箱模型(如无法解析的树模型)
如果收益是通过黑箱模型(比如预训练的XGBoost、随机森林)预测的,无法写出显式数学表达式,需要使用黑箱优化框架,推荐以下工具:
- Scipy Optimize:适合带约束的黑箱优化,示例代码:
import numpy as np from scipy.optimize import minimize def get_revenue(x): # 替换为你的树模型预测逻辑 return np.random.rand() * 1000 def total_revenue(x): mon, tues = x # 最大化问题转为最小化负值(适配scipy的minimize接口) return -(mon * get_revenue(mon) + tues * get_revenue(tues)) # 约束:mon + tues = 1 constraints = ({'type': 'eq', 'fun': lambda x: x[0] + x[1] - 1}) # 变量边界:0.2<=mon,tues<=0.8 bounds = ((0.2, 0.8), (0.2, 0.8)) # 初始猜测值 x0 = [0.5, 0.5] result = minimize(total_revenue, x0, method='SLSQP', bounds=bounds, constraints=constraints) print(f"最优分配:周一{result.x[0]:.4f},周二{result.x[1]:.4f}") print(f"最大收益:{-result.fun:.2f}")
- 贝叶斯优化工具:如
BayesianOptimization、Optuna,适合高维度、非凸的黑箱优化场景,能更高效地找到最优解。 - 进化算法框架:如
DEAP,适合复杂约束下的全局优化。
内容的提问来源于stack exchange,提问作者tjt

