C#解决组合优化问题

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#解决组合优化问题
Google Optimization Tools介绍
Google Optimization Tools(OR-Tools)是⼀款专门快速⽽便携地解决组合优化问题的套件。

它包含了:
约束编程求解器。

简单⽽统⼀的接⼝,⽤于多种线性规划和混合整数规划求解,包括 CBC、CLP、GLOP、GLPK、Gurobi、CPLEX 和SCIP。

图算法 (最短路径、最⼩成本、最⼤流量、线性求和分配)。

经典旅⾏推销员问题和车辆路径问题的算法。

经典装箱和背包算法。

Google使⽤C++开发了OR-Tools库,但⽀持Python,C#,或Java语⾔调⽤。

安装Google OR-Tools
Google OR-Tools的源码在[Github] google/or-tools。

其它开发环境下的安装如下。

Linux or Mac下安装
1. 确认使⽤了Python
2.7+,
3.5+版本,以及pip 9.0.1+版本。

2. Mac OSX系统需要安装命令⾏⼯具Xcode,在Terminal中执⾏xcode-select --install。

Linux系统需要安装g++,在Terminal中执⾏sudo apt-get install g++ make。

如果使⽤C#请确认安装了Mono 4.2.0+的64位版本。

3. 在Terminal中执⾏pip install --upgrade ortools直接安装Python版本的OR-Tools包。

C++/Java/C#版本的链接为:Mac, Ubuntu 17.04,
Ubuntu 16.04, Ubuntu 14.04, CentOS 7, Debian 9 ,下载到指定⽬录后执⾏make all。

Windows下安装
Python版本的包的安装和Linux⼀样,可⾃⾏选⽤合适的开发⼯具。

若是使⽤C++、C#,推荐使⽤64位版本的Windows10操作系统,并且使⽤Microsoft Visual Studio 2015 或者 2017作为开发⼯具,相应的库⽂件下载地址为: Visual Studio 2017 the Visual Studio 2015。

C++使⽤lib/ortools.lib, 并且将or‑tools/include添加到项⽬引⽤。

Java使⽤jar命令调⽤lib/com.google.ortools.lib的⽅式,并且将‑Djava.library.path=PATH_TO_or‑tools/lib添加到命令⾏。

C#添加bin/Google.OrTools.dll到项⽬依赖,或者使⽤NuGet搜索Google.OrTools进⾏安装。

Demo
以下是⼏种⽀持语⾔的demo,运⾏⼀下验证是否安装正确。

C++ 代码
复制代码
include "ortools/linear_solver/linear_solver.h"
include "ortools/linear_solver/linear_solver.pb.h"
namespace operations_research {
void RunTest(
MPSolver::OptimizationProblemType optimization_problem_type) {
MPSolver solver("Glop", optimization_problem_type);
MPVariable* const x = solver.MakeNumVar(0.0, 1, "x");
MPVariable* const y = solver.MakeNumVar(0.0, 2, "y");
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 1);
objective->SetCoefficient(y, 1);
objective->SetMaximization();
solver.Solve();
printf("\nSolution:");
printf("\nx = %.1f", x->solution_value());
printf("\ny = %.1f", y->solution_value());
}
void RunExample() {
RunTest(MPSolver::GLOP_LINEAR_PROGRAMMING);
}
}
int main(int argc, char** argv) {
operations_research::RunExample();
return 0;
}
复制代码
C# 代码
复制代码
using System;
using Google.OrTools.LinearSolver;
public class my_program
{
private static void RunLinearProgrammingExample(String solverType) {
Solver solver = Solver.CreateSolver("IntegerProgramming", solverType); Variable x = solver.MakeNumVar(0.0, 1.0, "x");
Variable y = solver.MakeNumVar(0.0, 2.0, "y");
Objective objective = solver.Objective();
objective.SetCoefficient(x, 1);
objective.SetCoefficient(y, 1);
objective.SetMaximization();
solver.Solve();
Console.WriteLine("Solution:");
Console.WriteLine("x = " + x.SolutionValue());
Console.WriteLine("y = " + y.SolutionValue());
}
static void Main()
{
RunLinearProgrammingExample("GLOP_LINEAR_PROGRAMMING"); }
}
复制代码
Python 代码
复制代码
from future import print_function
from ortools.linear_solver import pywraplp
def main():
solver = pywraplp.Solver('SolveSimpleSystem',
pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
x = solver.NumVar(0, 1, 'x')
y = solver.NumVar(0, 2, 'y')
objective = solver.Objective()
objective.SetCoefficient(x, 1)
objective.SetCoefficient(y, 1)
objective.SetMaximization()
solver.Solve()
print('Solution:')
print('x = ', x.solution_value())
print('y = ', y.solution_value())
if name == 'main':
main()
复制代码
Java 代码
复制代码
import com.google.ortools.linearsolver.MPConstraint;
import com.google.ortools.linearsolver.MPObjective;
import com.google.ortools.linearsolver.MPSolver;
import com.google.ortools.linearsolver.MPVariable;
public class my_program {
static { System.loadLibrary("jniortools"); }
private static MPSolver createSolver (String solverType) {
return new MPSolver("my_program",
MPSolver.OptimizationProblemType.valueOf(solverType));
}
private static void runmy_program(String solverType,
boolean printModel) {
MPSolver solver = createSolver(solverType);
MPVariable x = solver.makeNumVar(0.0, 1.0, "x");
MPVariable y = solver.makeNumVar(0.0, 2.0, "y");
MPObjective objective = solver.objective();
objective.setCoefficient(y, 1);
objective.setMaximization();
solver.solve();
System.out.println("Solution:");
System.out.println("x = " + x.solutionValue());
System.out.println("y = " + y.solutionValue());
}
public static void main(String[] args) throws Exception {
runmy_program("GLOP_LINEAR_PROGRAMMING", false);
}
}
复制代码
执⾏结果如图:
使⽤.Net Core与Google Optimization Tools实现员⼯排班计划Scheduling
让⼤家初步了解了Google Optimization Tools是⼀款约束求解(CP)的⾼效套件。

那么我们⽤.Net Core与Google Optimization Tools来实现⼀个有关员⼯排班计划的场景感受⼀下。

众所周知,现实⽣活中有些⼯作是7X24⼯作制的,如呼叫中⼼或医院护⼠,最常见的问题就是如何安排多名员⼯进⾏倒班,制定好⽇程时间表,使每班配备⾜够的⼈员来维持运营。

时间表有各种不同的约束要求,例如:员⼯不允许连续两次轮班之类。

接下来我们介绍类似问题的⼀个⽰例,叫护⼠调度问题,并展⽰了如何使⽤.Net Core与Google Optimization Tools实现排班计划。

护⼠调度问题
在本例中,医院主管需要为四名护⼠创建⼀个周时间表,具体情况如下:
每天分为早、中、晚三班轮班。

在每⼀天,所有护⼠都被分配到不同的班次,除了有⼀名护⼠可以休息。

每位护⼠每周⼯作五到六天。

每个班次不会有超过两名护⼠在⼯作。

如果⼀名护⼠某⼀天的班次是中班或晚班,她也必须在前⼀⽇或次⽇安排相同的班次。

有两种⽅式来描述我们需要解决的问题:
指派护⼠轮班
将班次分配给护⼠
事实证明,解决问题的最好⽅法是结合两种⽅式来求解。

指派护⼠轮班
下表显⽰了指派护⼠轮班视⾓的排班情况,这些护⼠被标记为A,B,C,D,换班,编号为0 - 3(其中0表⽰护⼠当天不⼯作)。

星期⽇
星期⼀星期⼆星期三星期四星期五星期六
班次1
A
B
A
A
A
A
A
班次2
C
C
C
B
B
B
B
班次3
D
D
D
D
C
C
D
将班次分配给护⼠
下表显⽰了将班次分配给护⼠视⾓的排班情况。

星期⽇星期⼀星期⼆星期三星期四星期五星期六
护⼠A 1 0 1 1 1 1 1
护⼠B 0 1 0 2 2 2 2
护⼠C 2 2 2 0 3 3 0
护⼠D 3 3 3 3 0 0 3
.Net Core解决⽅案
⾸先使⽤VS017创建⼀个.Net Core的控制台项⽬。

由于Google Optimization Tools对.Net Core的⽀持还不友好,需要通过NuGet引⽤⼀个第三⽅专门为Core编译好的程序集以及相关依赖,Google.OrTools.Core和CrossPlatformLibraryLoader。

准备完成后,我们逐⼀介绍编码的过程。

⾸先介绍⼏个基本概念:
IntVar是约束求解中使⽤最多的变量形式,⼀般约束问题中变化的对象都应该定义为⼀个类似在⼀定范围内整形数值的变量。

solver.MakeIntVar是创建约束求解中变量的⽅法,约束求解⼀定会定义⼀些可变化的对象,⼀般都需要转化成数值类型。

solver.Add是添加若⼲约束条件的⽅法。

solver.MakePhase定义了求解的⽬标以及求解的取值策略。

solver.Solve进⾏求解,并对指定的集合赋值。

solver.MakeAllSolutionCollector表⽰获取解的集合对象。

定义约束求解器和相关变量
我们⽤shift和nurse分别来表⽰班次和护⼠。

复制代码
// 创建约束求解器.
var solver = new Solver("schedule_shifts");
var num_nurses = 4;
var num_shifts = 4; // 班次数定为4,这样序号为0的班次表⽰是休息的班。

var num_days = 7;
// [START]
// 创建班次变量
var shifts = new Dictionary<(int, int), IntVar>();
foreach (var j in Enumerable.Range(0, num_nurses))
{
foreach (var i in Enumerable.Range(0, num_days))
{
// shifts[(j, i)]表⽰护⼠j在第i天的班次,可能的班次的编号范围是:[0, num_shifts)
shifts[(j, i)] = solver.MakeIntVar(0, num_shifts - 1, string.Format("shifts({0},{1})", j, i));
}
}
// 将变量集合转成扁平化数组
var shifts_flat = (from j in Enumerable.Range(0, num_nurses)
from i in Enumerable.Range(0, num_days)
select shifts[(j, i)]).ToArray();
// 创建护⼠变量
var nurses = new Dictionary<(int, int), IntVar>();
foreach (var j in Enumerable.Range(0, num_shifts))
{
foreach (var i in Enumerable.Range(0, num_days))
{
// nurses[(j, i)]表⽰班次j在第i天的当班护⼠,可能的护⼠的编号范围是:[0, num_nurses)
nurses[(j, i)] = solver.MakeIntVar(0, num_nurses - 1, string.Format("shift{0} day{1}", j, i));
}
}
复制代码
shifts和nurses两个对象含义如下:
shifts[(j, i)]表⽰护⼠j在第i天的班次,可能的班次的编号范围是:[0, num_shifts)。

nurses[(j, i)]表⽰班次j在第i天的当班护⼠,可能的护⼠的编号范围是:[0, num_nurses)。

shifts_flat是将shifts的Values简单地处理成扁平化,后⾯直接⽤于当参数传给约束求解器solver以指定需要求解的变量。

定义shifts和nurses的对应关系
将每⼀天的nurses单独列出来,按照编号顺序扁平化成⼀个数组对象,s.IndexOf(nurses_for_day)是⼀种OR-Tools要求的特定⽤法,相当于nurses_for_day[s]求值。

这⾥利⽤了s的值恰好是在nurses_for_day中对应nurse的编号。

注意这⾥的两层foreach循环,v外层不能互换,必须是现在这样,内层循环的主体对象与shifts_flat⼀致。

复制代码
// 定义shifts和nurses之前的关联关系
foreach (var day in Enumerable.Range(0, num_days))
{
var nurses_for_day = (from j in Enumerable.Range(0, num_shifts)
select nurses[(j, day)]).ToArray();
foreach (var j in Enumerable.Range(0, num_nurses))
{
var s = shifts[(j, day)];
// s.IndexOf(nurses_for_day)相当于nurses_for_day[s]
// 这⾥利⽤了s的值恰好是在nurses_for_day中对应nurse的编号
solver.Add(s.IndexOf(nurses_for_day) == j);
}
}
复制代码
定义护⼠在不同的班次当班约束
AllDifferent⽅法是OR-Tools定义约束的⽅法之⼀,表⽰指定的IntVar数组在进⾏计算时受唯⼀性制约。

满⾜每⼀天的当班护⼠不重复,即每⼀天的班次不会出现重复的护⼠的约束条件,同样每⼀个护⼠每天不可能同时轮值不同的班次。

复制代码
// 满⾜每⼀天的当班护⼠不重复,每⼀天的班次不会出现重复的护⼠的约束条件
// 同样每⼀个护⼠每天不可能同时轮值不同的班次
foreach (var i in Enumerable.Range(0, num_days))
{
solver.Add((from j in Enumerable.Range(0, num_nurses)
select shifts[(j, i)]).ToArray().AllDifferent());
solver.Add((from j in Enumerable.Range(0, num_shifts)
select nurses[(j, i)]).ToArray().AllDifferent());
}
复制代码
定义护⼠每周当班次数的约束
Sum⽅法是OR-Tools定义运算的⽅法之⼀。

注意shifts[(j, i)] > 0运算被重载过,其返回类型是WrappedConstraint⽽不是默认的bool。

满⾜每个护⼠在⼀周范围内只出现[5, 6]次。

复制代码
// 满⾜每个护⼠在⼀周范围内只出现[5, 6]次
foreach (var j in Enumerable.Range(0, num_nurses))
{
solver.Add((from i in Enumerable.Range(0, num_days)
select shifts[(j, i)] > 0).ToArray().Sum() >= 5);
solver.Add((from i in Enumerable.Range(0, num_days)
select shifts[(j, i)] > 0).ToArray().Sum() <= 6);
}
复制代码
定义每个班次在⼀周内当班护⼠⼈数的约束
Max⽅法是OR-Tools定义运算的⽅法之⼀,表⽰对指定的IntVar数组求最⼤值。

注意MakeBoolVar⽅法返回类型是IntVar⽽不是默认的bool,works_shift[(i, j)]为True表⽰护⼠i在班次j⼀周内⾄少要有1次,BoolVar类型的变量最终取值是0或1,同样也表⽰了False或True。

满⾜每个班次⼀周内不会有超过两名护⼠当班⼯作。

复制代码
// 创建⼀个⼯作的变量,works_shift[(i, j)]为True表⽰护⼠i在班次j⼀周内⾄少要有1次
// BoolVar类型的变量最终取值是0或1,同样也表⽰了False或True
var works_shift = new Dictionary<(int, int), IntVar>();
foreach (var i in Enumerable.Range(0, num_nurses))
{
foreach (var j in Enumerable.Range(0, num_shifts))
{
works_shift[(i, j)] = solver.MakeBoolVar(string.Format("nurse%d shift%d", i, j));
}
}
foreach (var i in Enumerable.Range(0, num_nurses))
{
foreach (var j in Enumerable.Range(0, num_shifts))
{
// 建⽴works_shift与shifts的关联关系
// ⼀周内的值要么为0要么为1,所以Max定义的约束是最⼤值,恰好也是0或1,1表⽰⾄少在每周轮班⼀天
solver.Add(works_shift[(i, j)] == (from k in Enumerable.Range(0, num_days)
select shifts[(i, k)].IsEqual(j)).ToArray().Max());
}
}
// 对于每个编号不为0的shift, 满⾜⾄少每周最多同⼀个班次2个护⼠当班
foreach (var j in Enumerable.Range(1, num_shifts - 1))
{
solver.Add((from i in Enumerable.Range(0, num_nurses)
select works_shift[(i, j)]).ToArray().Sum() <= 2);
}
复制代码
定义护⼠在中班和晚班的连班约束
复制代码
// 满⾜中班或晚班的护⼠前⼀天或后⼀天也是相同的班次
// ⽤nurses的key中Tuple类型第1个item的值表⽰shift为2或3
// shift为1表⽰早班班次,shift为0表⽰休息的班次
solver.Add(solver.MakeMax(nurses[(2, 0)] == nurses[(2, 1)], nurses[(2, 1)] == nurses[(2, 2)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 1)] == nurses[(2, 2)], nurses[(2, 2)] == nurses[(2, 3)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 2)] == nurses[(2, 3)], nurses[(2, 3)] == nurses[(2, 4)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 3)] == nurses[(2, 4)], nurses[(2, 4)] == nurses[(2, 5)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 4)] == nurses[(2, 5)], nurses[(2, 5)] == nurses[(2, 6)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 5)] == nurses[(2, 6)], nurses[(2, 6)] == nurses[(2, 0)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 6)] == nurses[(2, 0)], nurses[(2, 0)] == nurses[(2, 1)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 0)] == nurses[(3, 1)], nurses[(3, 1)] == nurses[(3, 2)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 1)] == nurses[(3, 2)], nurses[(3, 2)] == nurses[(3, 3)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 2)] == nurses[(3, 3)], nurses[(3, 3)] == nurses[(3, 4)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 3)] == nurses[(3, 4)], nurses[(3, 4)] == nurses[(3, 5)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 4)] == nurses[(3, 5)], nurses[(3, 5)] == nurses[(3, 6)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 5)] == nurses[(3, 6)], nurses[(3, 6)] == nurses[(3, 0)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 6)] == nurses[(3, 0)], nurses[(3, 0)] == nurses[(3, 1)]) == 1);
复制代码
定义约束求解器的使⽤
复制代码
// 将变量集合设置为求解的⽬标,Solver有⼀系列的枚举值,可以指定求解的选择策略。

var db = solver.MakePhase(shifts_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); // 创建求解的对象
var solution = solver.MakeAssignment();
solution.Add(shifts_flat);
var collector = solver.MakeAllSolutionCollector(solution);
复制代码
执⾏求解计算并显⽰结果
复制代码
solver.Solve(db, new[] { collector });
Console.WriteLine("Solutions found: {0}", collector.SolutionCount());
Console.WriteLine("Time: {0}ms", solver.WallTime());
Console.WriteLine();
// 显⽰⼀些随机的结果
var a_few_solutions = new[] { 340, 2672, 7054 };
foreach (var sol in a_few_solutions)
{
Console.WriteLine("Solution number {0}", sol);
foreach (var i in Enumerable.Range(0, num_days))
{
Console.WriteLine("Day {0}", i);
foreach (var j in Enumerable.Range(0, num_nurses))
{
Console.WriteLine("Nurse {0} assigned to task {1}", j, collector.Value(sol, shifts[(j, i)])); }
Console.WriteLine();
}
}
复制代码
运⾏结果如下:
最后,放出完整的代码如下
复制代码
using Google.OrTools.ConstraintSolver;
using System;
using System.Collections.Generic;
using System.Linq;
public class ConsoleApp1
{
static void Main()
{
// 创建约束求解器.
var solver = new Solver("schedule_shifts");
var num_nurses = 4;
var num_shifts = 4; // 班次数定为4,这样序号为0的班次表⽰是休息的班。

var num_days = 7;
// [START]
// 创建班次变量
var shifts = new Dictionary<(int, int), IntVar>();
foreach (var j in Enumerable.Range(0, num_nurses))
{
foreach (var i in Enumerable.Range(0, num_days))
{
// shifts[(j, i)]表⽰护⼠j在第i天的班次,可能的班次的编号范围是:[0, num_shifts)
shifts[(j, i)] = solver.MakeIntVar(0, num_shifts - 1, string.Format("shifts({0},{1})", j, i));
}
}
// 将变量集合转成扁平化数组
var shifts_flat = (from j in Enumerable.Range(0, num_nurses)
from i in Enumerable.Range(0, num_days)
select shifts[(j, i)]).ToArray();
// 创建护⼠变量
var nurses = new Dictionary<(int, int), IntVar>();
foreach (var j in Enumerable.Range(0, num_shifts))
{
foreach (var i in Enumerable.Range(0, num_days))
{
// nurses[(j, i)]表⽰班次j在第i天的当班护⼠,可能的护⼠的编号范围是:[0, num_nurses) nurses[(j, i)] = solver.MakeIntVar(0, num_nurses - 1, string.Format("shift{0} day{1}", j, i)); }
}
// 定义shifts和nurses之前的关联关系
foreach (var day in Enumerable.Range(0, num_days))
{
var nurses_for_day = (from j in Enumerable.Range(0, num_shifts)
select nurses[(j, day)]).ToArray();
foreach (var j in Enumerable.Range(0, num_nurses))
{
var s = shifts[(j, day)];
// s.IndexOf(nurses_for_day)相当于nurses_for_day[s]
// 这⾥利⽤了s的值恰好是在nurses_for_day中对应nurse的编号
solver.Add(s.IndexOf(nurses_for_day) == j);
}
}
// 满⾜每⼀天的当班护⼠不重复,每⼀天的班次不会出现重复的护⼠的约束条件
// 同样每⼀个护⼠每天不可能同时轮值不同的班次
foreach (var i in Enumerable.Range(0, num_days))
{
solver.Add((from j in Enumerable.Range(0, num_nurses)
select shifts[(j, i)]).ToArray().AllDifferent());
solver.Add((from j in Enumerable.Range(0, num_shifts)
select nurses[(j, i)]).ToArray().AllDifferent());
}
// 满⾜每个护⼠在⼀周范围内只出现[5, 6]次
foreach (var j in Enumerable.Range(0, num_nurses))
{
solver.Add((from i in Enumerable.Range(0, num_days)
select shifts[(j, i)] > 0).ToArray().Sum() >= 5);
solver.Add((from i in Enumerable.Range(0, num_days)
select shifts[(j, i)] > 0).ToArray().Sum() <= 6);
}
// 创建⼀个⼯作的变量,works_shift[(i, j)]为True表⽰护⼠i在班次j⼀周内⾄少要有1次
// BoolVar类型的变量最终取值是0或1,同样也表⽰了False或True
var works_shift = new Dictionary<(int, int), IntVar>();
foreach (var i in Enumerable.Range(0, num_nurses))
{
foreach (var j in Enumerable.Range(0, num_shifts))
{
works_shift[(i, j)] = solver.MakeBoolVar(string.Format("nurse%d shift%d", i, j));
}
}
foreach (var i in Enumerable.Range(0, num_nurses))
{
foreach (var j in Enumerable.Range(0, num_shifts))
{
// 建⽴works_shift与shifts的关联关系
// ⼀周内的值要么为0要么为1,所以Max定义的约束是最⼤值,恰好也是0或1,1表⽰⾄少在每周轮班⼀天 solver.Add(works_shift[(i, j)] == (from k in Enumerable.Range(0, num_days)
select shifts[(i, k)].IsEqual(j)).ToArray().Max());
}
}
// 对于每个编号不为0的shift, 满⾜⾄少每周最多同⼀个班次2个护⼠当班
foreach (var j in Enumerable.Range(1, num_shifts - 1))
{
solver.Add((from i in Enumerable.Range(0, num_nurses)
select works_shift[(i, j)]).ToArray().Sum() <= 2);
}
// 满⾜中班或晚班的护⼠前⼀天或后⼀天也是相同的班次
// ⽤nurses的key中Tuple类型第1个item的值表⽰shift为2或3
// shift为1表⽰早班班次,shift为0表⽰休息的班次
solver.Add(solver.MakeMax(nurses[(2, 0)] == nurses[(2, 1)], nurses[(2, 1)] == nurses[(2, 2)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 1)] == nurses[(2, 2)], nurses[(2, 2)] == nurses[(2, 3)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 2)] == nurses[(2, 3)], nurses[(2, 3)] == nurses[(2, 4)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 3)] == nurses[(2, 4)], nurses[(2, 4)] == nurses[(2, 5)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 4)] == nurses[(2, 5)], nurses[(2, 5)] == nurses[(2, 6)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 5)] == nurses[(2, 6)], nurses[(2, 6)] == nurses[(2, 0)]) == 1);
solver.Add(solver.MakeMax(nurses[(2, 6)] == nurses[(2, 0)], nurses[(2, 0)] == nurses[(2, 1)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 0)] == nurses[(3, 1)], nurses[(3, 1)] == nurses[(3, 2)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 1)] == nurses[(3, 2)], nurses[(3, 2)] == nurses[(3, 3)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 2)] == nurses[(3, 3)], nurses[(3, 3)] == nurses[(3, 4)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 3)] == nurses[(3, 4)], nurses[(3, 4)] == nurses[(3, 5)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 4)] == nurses[(3, 5)], nurses[(3, 5)] == nurses[(3, 6)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 5)] == nurses[(3, 6)], nurses[(3, 6)] == nurses[(3, 0)]) == 1);
solver.Add(solver.MakeMax(nurses[(3, 6)] == nurses[(3, 0)], nurses[(3, 0)] == nurses[(3, 1)]) == 1);
// 将变量集合设置为求解的⽬标,Solver有⼀系列的枚举值,可以指定求解的选择策略。

var db = solver.MakePhase(shifts_flat, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE); // 创建求解的对象
var solution = solver.MakeAssignment();
solution.Add(shifts_flat);
var collector = solver.MakeAllSolutionCollector(solution);
solver.Solve(db, new[] { collector });
Console.WriteLine("Solutions found: {0}", collector.SolutionCount());
Console.WriteLine("Time: {0}ms", solver.WallTime());
Console.WriteLine();
// 显⽰⼀些随机的结果
var a_few_solutions = new[] { 340, 2672, 7054 };
foreach (var sol in a_few_solutions)
{
Console.WriteLine("Solution number {0}", sol);
foreach (var i in Enumerable.Range(0, num_days))
{
Console.WriteLine("Day {0}", i);
foreach (var j in Enumerable.Range(0, num_nurses))
{
Console.WriteLine("Nurse {0} assigned to task {1}", j, collector.Value(sol, shifts[(j, i)])); }
Console.WriteLine();
}
}
}
}
复制代码。

相关文档
最新文档