基于Google OR-Tools实现分组标准差最小化的代码求助
学生分组优化问题(Google OR-Tools)
我正在使用Google OR-Tools编写程序,将若干学生分配至数量可变的小组中。每位学生可选择一位希望同组的“好友”。
约束条件:
- 每位学生必须被分配至一个小组;
- 要求选择同组的学生必须被分配到同一小组。
我希望通过最小化各组学生数量的标准差来提升分配的公平性,目前已完成基础约束的代码,但卡在了实现最小化目标的环节,请求提供对应的示例代码。
现有代码(C#)
Student student = new Student("123", "Sam", null); Student student2 = new Student("234", "Jos", student); Student student3 = new Student("345", "Jef", null); Student student4 = new Student("456", "Jane", student3); List<Student> students = new List<Student>() { student, student2, student3, student4 }; const int GROUPS = 2; CpModel model = new CpModel(); // 创建分配变量:assignments[i,j]为1表示第i个学生分配到第j组 BoolVar[,] assignments = new BoolVar[students.Count,GROUPS]; for(int i = 0; i < students.Count; i++) { for(int j = 0; j < GROUPS; j++) { assignments[i, j] = model.NewBoolVar($"assingments_{i}_{j}"); } } // 约束:每个学生必须分配到恰好一个小组 for(int i = 0; i < students.Count; i++) { IList<ILiteral> groupsForStudent = new List<ILiteral>(); for(int j = 0; j < GROUPS; j++) { groupsForStudent.Add(assignments[i, j]); } model.AddExactlyOne(groupsForStudent); } // 约束:好友必须分配到同一小组 for(int i = 0; i < students.Count; i++) { Student first = students[i]; if(first.Friend != null) { int otherIdx = students.IndexOf(first.Friend); for(int j = 0; j < GROUPS; j++) { model.AddAbsEquality(assignments[i, j], assignments[otherIdx, j]); } } }
解决方案:添加最小化标准差的目标函数
标准差的优化等价于最小化各组人数与平均值差的平方和(标准差是平方和的平方根,两者优化方向完全一致),这种方式更适配整数规划模型的处理逻辑。以下是完整的补充代码:
// 计算总学生数与理想分组人数(平均值) int totalStudents = students.Count; int avgSize = totalStudents / GROUPS; // 定义每个小组的人数变量 IntVar[] groupSizes = new IntVar[GROUPS]; for (int j = 0; j < GROUPS; j++) { groupSizes[j] = model.NewIntVar(0, totalStudents, $"group_size_{j}"); // 约束:组人数等于分配到该组的学生数量之和 model.Add(LinearExpr.Sum(Enumerable.Range(0, students.Count).Select(i => assignments[i, j])) == groupSizes[j]); } // 计算组人数与平均值差的平方和 IntVar sumOfSquares = model.NewIntVar(0, totalStudents * totalStudents, "sum_of_squares"); LinearExpr sumExpr = new LinearExpr(); for (int j = 0; j < GROUPS; j++) { // 计算 groupSizes[j]^2 IntVar sizeSquared = model.NewIntVar(0, totalStudents * totalStudents, $"size_squared_{j}"); model.AddMultiplicationEquality(sizeSquared, groupSizes[j], groupSizes[j]); // 累加 (size - avg)^2 = size² - 2*avg*size + avg² sumExpr.Add(sizeSquared); sumExpr.AddTerm(groupSizes[j], -2 * avgSize); } sumExpr.Add(avgSize * avgSize * GROUPS); model.Add(sumExpr == sumOfSquares); // 设置目标:最小化平方和(等价于最小化标准差) model.Minimize(sumOfSquares); // 求解模型并输出结果 CpSolver solver = new CpSolver(); CpSolverStatus status = solver.Solve(model); if (status == CpSolverStatus.Optimal || status == CpSolverStatus.Feasible) { Console.WriteLine("最优分配结果:"); for (int j = 0; j < GROUPS; j++) { Console.WriteLine($"组 {j+1} 人数:{solver.Value(groupSizes[j])}"); Console.WriteLine("成员列表:"); for (int i = 0; i < students.Count; i++) { if (solver.BooleanValue(assignments[i, j])) { Console.WriteLine($" - {students[i].Name} (ID: {students[i].Id})"); } } Console.WriteLine(); } Console.WriteLine($"组人数与平均值差的平方和:{solver.Value(sumOfSquares)}"); } else { Console.WriteLine("未找到可行分配方案"); }
关键说明
- 用平方和代替标准差:避免浮点运算,适配OR-Tools的整数变量处理机制,且优化目标完全等价
- 变量定义:通过
LinearExpr.Sum计算每组的实际人数,建立分配变量与组人数的关联 - 平方计算:使用
AddMultiplicationEquality实现变量的平方运算,符合CP-Solver的约束定义规则
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

