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
相关产品推荐
相关产品推荐

