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

Python OR-Tools约束编程变量设置与约束编写问题

OR-Tools 人员组合约束实现方案

你原来的变量仅标记员工是否被选中,没有处理员工可归属多个分组的场景,会导致跨组员工重复占用分组名额。最直接的实现方式是为每个「员工-可分配分组」对创建独立布尔变量,再依次落地三类约束即可。

完整可运行代码

from ortools.sat.python import cp_model
import pandas as pd

model = cp_model.CpModel()

# 样本数据集
df = pd.DataFrame([['John', 'A',5], ['Andy', 'A/C', 10], ['Michelle', 'B/C', 15], 
                   ['Jennifer', 'B', 5], ['Frank', 'C',10], ['Zack','A',5]],
                   columns=['Employee','Group','Salary'])

# 变量定义:为每个(员工, 可归属分组)创建布尔变量,表示该员工被分配到对应组
group_vars = {'A': [], 'B': [], 'C': []}  # 每个分组对应的分配变量集合
emp_vars = {}  # 每个员工对应的分配变量集合
var_info = {} # 变量到(员工名, 薪资, 分组)的映射
for _, row in df.iterrows():
    emp = row['Employee']
    sal = row['Salary']
    valid_groups = row['Group'].split('/')
    current_emp_vars = []
    for g in valid_groups:
        v = model.NewBoolVar(f'{emp}_{g}')
        group_vars[g].append(v)
        current_emp_vars.append(v)
        var_info[v] = (emp, sal, g)
    emp_vars[emp] = current_emp_vars

# --------------------------
# 约束1:A组选2人,B/C组各选1人
# --------------------------
model.Add(sum(group_vars['A']) == 2)
model.Add(sum(group_vars['B']) == 1)
model.Add(sum(group_vars['C']) == 1)

# --------------------------
# 约束2:每个员工最多被选用一次
# --------------------------
for v_list in emp_vars.values():
    model.Add(sum(v_list) <= 1)

# --------------------------
# 约束3:总薪资在[25,30]区间
# --------------------------
total_salary = sum(v * var_info[v][1] for v in var_info.keys())
model.Add(total_salary >= 25)
model.Add(total_salary <= 30)

# 解收集器:存储所有可行解
class SolutionCollector(cp_model.CpSolverSolutionCallback):
    def __init__(self, var_info, total_salary_expr):
        super().__init__()
        self.var_info = var_info
        self.total_salary_expr = total_salary_expr
        self.solutions = []
    
    def on_solution_callback(self):
        res = {'A': [], 'B': [], 'C': []}
        for v in self.var_info:
            if self.Value(v) == 1:
                emp, _, g = self.var_info[v]
                res[g].append(emp)
        self.solutions.append( (res, self.Value(self.total_salary_expr)) )

# 求解所有可行解
solver = cp_model.CpSolver()
collector = SolutionCollector(var_info, total_salary)
solver.SearchForAllSolutions(model, collector)

# 打印结果
print(f'共找到{len(collector.solutions)}个可行组合:')
for idx, (group_assign, sal) in enumerate(collector.solutions, 1):
    print(f'\n组合{idx},总薪资:{sal}')
    for g in ['A', 'B', 'C']:
        print(f'  {g}组:{", ".join(group_assign[g])}')

约束实现逻辑说明

  • 分组人数约束:直接对每个分组下的所有分配变量求和,匹配要求的人数即可,不会出现跨组员工重复计数的问题。
  • 员工不重复约束:每个员工名下的所有分配变量求和不超过1,保证员工最多被分配到一个分组,不会重复出现在组合中。
  • 薪资范围约束:每个分配变量乘以对应员工的薪资得到总薪资表达式,直接添加上下界约束即可。

运行代码后会输出5个符合要求的可行组合,你可以直接扩展这个逻辑适配更大规模的数据集。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:30:45