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

基于or-tools实现约束空间无偏随机化的可扩展方案咨询

问题描述

我使用or-tools编写了一段简单代码,目标是随机生成3个变量,满足三者之和为定值。我通过添加随机hint的方式期望求解器每次返回不同结果,但统计取值频率后发现,仅第一个变量无偏符合均匀分布,其余变量均偏向0,分布存在偏差。

代码如下:

from ortools.sat.python import cp_model
import numpy as np
import matplotlib.pyplot as plt

model = cp_model.CpModel()

mx = 500
x = model.NewIntVar(0, mx, 'x')
y = model.NewIntVar(0, mx, 'y')
z = model.NewIntVar(0, mx, 'z')

freqX = [0] * (mx + 1)
freqY = [0] * (mx + 1)
freqZ = [0] * (mx + 1)

model.Add(x + y + z == mx)
solver = cp_model.CpSolver()
solver.parameters.cp_model_presolve = False

for i in range(10000):
    model.ClearAssumptions()
    model.ClearHints()
    model.AddHint(x, np.random.randint(0, mx))
    model.AddHint(y, np.random.randint(0, mx))
    model.AddHint(z, np.random.randint(0, mx))
    
    status = solver.Solve(model)
    
    freqX[solver.Value(x)] += 1
    freqY[solver.Value(y)] += 1
    freqZ[solver.Value(z)] += 1

plt.subplot(311)
plt.bar(range(mx+1), freqX, width=1.0)
plt.subplot(312)
plt.bar(range(mx+1), freqY, width=1.0)
plt.subplot(313)
plt.bar(range(mx+1), freqZ, width=1.0)
plt.savefig('foo.svg')

输出分布如下图:
变量分布示意图

我已尝试评论区建议的无效hint方案,得到的分布如下图:
无效hint对应分布示意图

请问有什么可扩展性最高的方法,可以让约束下的所有变量都服从无偏均匀分布?


解决方案

偏差原因

当前方案出现偏差的核心原因是:CP-SAT求解器的AddHint仅作为搜索优先级的参考,而非强制约束,求解器默认的变量分支顺序与变量定义顺序绑定,会优先满足先定义变量的hint要求,剩余变量的取值空间被大幅压缩,最终出现后定义变量偏向0的偏态分布。

可扩展性最高的通用方案

如果需要适配任意复杂约束场景(不止是三变量和为定值的简单场景),随机线性目标采样法的可扩展性最高,不需要针对约束类型做定制修改,直接适配所有CP-SAT支持的约束规则,数学上可以保证解的无偏均匀性:

  • 每次采样前,为所有变量分配随机权重,将最大化(或最小化)该随机权重的线性组合作为临时优化目标
  • 搭配随机分支策略和动态随机种子,避免求解器搜索逻辑的确定性偏差

修改后代码示例

from ortools.sat.python import cp_model
import numpy as np
import matplotlib.pyplot as plt

model = cp_model.CpModel()
mx = 500
x = model.NewIntVar(0, mx, 'x')
y = model.NewIntVar(0, mx, 'y')
z = model.NewIntVar(0, mx, 'z')
model.Add(x + y + z == mx)

freqX = [0] * (mx + 1)
freqY = [0] * (mx + 1)
freqZ = [0] * (mx + 1)

solver = cp_model.CpSolver()
solver.parameters.cp_model_presolve = False
# 开启随机分支策略
solver.parameters.search_branching = cp_model.CpSolverParameters.RANDOM_BRANCHING

for i in range(10000):
    # 清空上一轮的目标函数
    model.ClearObjective()
    # 生成随机权重,构造随机线性目标
    w_x, w_y, w_z = np.random.rand(3)
    model.Maximize(w_x * x + w_y * y + w_z * z)
    # 每次使用不同的随机种子
    solver.parameters.random_seed = i
    
    status = solver.Solve(model)
    
    freqX[solver.Value(x)] += 1
    freqY[solver.Value(y)] += 1
    freqZ[solver.Value(z)] += 1

# 绘图部分不变
plt.subplot(311)
plt.bar(range(mx+1), freqX, width=1.0)
plt.subplot(312)
plt.bar(range(mx+1), freqY, width=1.0)
plt.subplot(313)
plt.bar(range(mx+1), freqZ, width=1.0)
plt.savefig('foo.svg')

特定场景的高效方案

如果你的场景固定为「N个非负整数和为定值」的约束,不需要适配其他约束,直接用断点插分法即可,不需要调用求解器,效率是最高的,且数学上严格均匀:

import numpy as np
mx = 500
for _ in range(10000):
    # 生成2个不重复的随机断点
    p1, p2 = np.random.choice(range(1, mx), size=2, replace=False)
    if p1 > p2:
        p1, p2 = p2, p1
    x, y, z = p1, p2 - p1, mx - p2
    # 统计频率即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 09:12:00