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();
        }
    }
}

}
復制代碼

http://www.cnblogs.com/BeanHsiang/p/8663625.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM