1 | #region License Information
|
---|
2 | /* HeuristicLab
|
---|
3 | * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
4 | *
|
---|
5 | * This file is part of HeuristicLab.
|
---|
6 | *
|
---|
7 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
8 | * it under the terms of the GNU General Public License as published by
|
---|
9 | * the Free Software Foundation, either version 3 of the License, or
|
---|
10 | * (at your option) any later version.
|
---|
11 | *
|
---|
12 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
15 | * GNU General Public License for more details.
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License
|
---|
18 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
19 | */
|
---|
20 | #endregion
|
---|
21 |
|
---|
22 | using HeuristicLab.Common;
|
---|
23 | using HeuristicLab.Data;
|
---|
24 |
|
---|
25 | namespace HeuristicLab.Problems.LinearAssignment {
|
---|
26 | public static class LinearAssignmentProblemSolver {
|
---|
27 | private const int UNASSIGNED = -1;
|
---|
28 |
|
---|
29 | /// <summary>
|
---|
30 | /// Uses the Hungarian algorithm to solve the linear assignment problem (LAP).
|
---|
31 | /// The LAP is defined as minimize f(p) = Sum(i = 1..N, c_{i, p(i)}) for a permutation p and an NxN cost matrix.
|
---|
32 | ///
|
---|
33 | /// The runtime complexity of the algorithm is O(n^3). The algorithm is deterministic and terminates
|
---|
34 | /// returning one of the optimal solutions and the corresponding quality.
|
---|
35 | /// </summary>
|
---|
36 | /// <remarks>
|
---|
37 | /// The algorithm is written similar to the fortran implementation given in http://www.seas.upenn.edu/qaplib/code.d/qapglb.f
|
---|
38 | /// </remarks>
|
---|
39 | /// <param name="costs">An NxN costs matrix.</param>
|
---|
40 | /// <param name="quality">The quality value of the optimal solution.</param>
|
---|
41 | /// <returns>The optimal solution.</returns>
|
---|
42 | public static int[] Solve(DoubleMatrix costs, out double quality) {
|
---|
43 | int length = costs.Rows;
|
---|
44 | // solve the linear assignment problem f(p) = Sum(i = 1..|p|, c_{i, p(i)})
|
---|
45 |
|
---|
46 | int[] rowAssign = new int[length], colAssign = new int[length];
|
---|
47 | double[] dualCol = new double[length], dualRow = new double[length];
|
---|
48 | for (int i = 0; i < length; i++) { // mark all positions as untouched
|
---|
49 | rowAssign[i] = UNASSIGNED;
|
---|
50 | colAssign[i] = UNASSIGNED;
|
---|
51 | }
|
---|
52 |
|
---|
53 | for (int i = 0; i < length; i++) { // find the minimum (base) level for each row
|
---|
54 | double min = costs[i, 0];
|
---|
55 | int minCol = 0;
|
---|
56 | dualCol[0] = min;
|
---|
57 | for (int j = 1; j < length; j++) {
|
---|
58 | if (costs[i, j] <= min) {
|
---|
59 | min = costs[i, j];
|
---|
60 | minCol = j;
|
---|
61 | }
|
---|
62 | if (costs[i, j] > dualCol[j])
|
---|
63 | dualCol[j] = costs[i, j];
|
---|
64 | }
|
---|
65 | dualRow[i] = min; // this will be the value of our dual variable
|
---|
66 | if (colAssign[minCol] == UNASSIGNED) {
|
---|
67 | colAssign[minCol] = i;
|
---|
68 | rowAssign[i] = minCol;
|
---|
69 | }
|
---|
70 | }
|
---|
71 |
|
---|
72 | for (int j = 0; j < length; j++) { // calculate the second dual variable
|
---|
73 | if (colAssign[j] != UNASSIGNED) dualCol[j] = 0;
|
---|
74 | else {
|
---|
75 | int minRow = 0;
|
---|
76 | for (int i = 0; i < length; i++) {
|
---|
77 | if (dualCol[j] > 0 && costs[i, j] - dualRow[i] < dualCol[j]) {
|
---|
78 | dualCol[j] = costs[i, j] - dualRow[i]; // the value is the original costs minus the first dual value
|
---|
79 | minRow = i;
|
---|
80 | }
|
---|
81 | }
|
---|
82 | if (rowAssign[minRow] == UNASSIGNED) {
|
---|
83 | colAssign[j] = minRow;
|
---|
84 | rowAssign[minRow] = j;
|
---|
85 | }
|
---|
86 | }
|
---|
87 | }
|
---|
88 |
|
---|
89 | // at this point costs_ij - dualRow_i - dualColumn_j results in a matrix that has at least one zero in every row and every column
|
---|
90 |
|
---|
91 | for (int i = 0; i < length; i++) { // try to make the remaining assignments
|
---|
92 | if (rowAssign[i] == UNASSIGNED) {
|
---|
93 | double min = dualRow[i];
|
---|
94 | for (int j = 0; j < length; j++) {
|
---|
95 | if (colAssign[j] == UNASSIGNED && (costs[i, j] - min - dualCol[j]).IsAlmost(0.0)) {
|
---|
96 | rowAssign[i] = j;
|
---|
97 | colAssign[j] = i;
|
---|
98 | break;
|
---|
99 | }
|
---|
100 | }
|
---|
101 | }
|
---|
102 | }
|
---|
103 |
|
---|
104 | bool[] marker = new bool[length];
|
---|
105 | double[] dplus = new double[length], dminus = new double[length];
|
---|
106 | int[] rowMarks = new int[length];
|
---|
107 |
|
---|
108 | for (int u = 0; u < length; u++) {
|
---|
109 | if (rowAssign[u] == UNASSIGNED) {
|
---|
110 | for (int i = 0; i < length; i++) {
|
---|
111 | rowMarks[i] = u;
|
---|
112 | marker[i] = false;
|
---|
113 | dplus[i] = double.MaxValue;
|
---|
114 | dminus[i] = costs[u, i] - dualRow[u] - dualCol[i];
|
---|
115 | }
|
---|
116 |
|
---|
117 | dplus[u] = 0;
|
---|
118 | int index = -1;
|
---|
119 | double minD = double.MaxValue;
|
---|
120 | while (true) {
|
---|
121 | minD = double.MaxValue;
|
---|
122 | for (int i = 0; i < length; i++) {
|
---|
123 | if (!marker[i] && dminus[i] < minD) {
|
---|
124 | minD = dminus[i];
|
---|
125 | index = i;
|
---|
126 | }
|
---|
127 | }
|
---|
128 |
|
---|
129 | if (colAssign[index] == UNASSIGNED) break;
|
---|
130 | marker[index] = true;
|
---|
131 | dplus[colAssign[index]] = minD;
|
---|
132 | for (int i = 0; i < length; i++) {
|
---|
133 | if (marker[i]) continue;
|
---|
134 | double compare = minD + costs[colAssign[index], i] - dualCol[i] - dualRow[colAssign[index]];
|
---|
135 | if (dminus[i] > compare) {
|
---|
136 | dminus[i] = compare;
|
---|
137 | rowMarks[i] = colAssign[index];
|
---|
138 | }
|
---|
139 | }
|
---|
140 |
|
---|
141 | } // while(true)
|
---|
142 |
|
---|
143 | while (true) {
|
---|
144 | colAssign[index] = rowMarks[index];
|
---|
145 | var ind = rowAssign[rowMarks[index]];
|
---|
146 | rowAssign[rowMarks[index]] = index;
|
---|
147 | if (rowMarks[index] == u) break;
|
---|
148 |
|
---|
149 | index = ind;
|
---|
150 | }
|
---|
151 |
|
---|
152 | for (int i = 0; i < length; i++) {
|
---|
153 | if (dplus[i] < double.MaxValue)
|
---|
154 | dualRow[i] += minD - dplus[i];
|
---|
155 | if (dminus[i] < minD)
|
---|
156 | dualCol[i] += dminus[i] - minD;
|
---|
157 | }
|
---|
158 | }
|
---|
159 | }
|
---|
160 |
|
---|
161 | quality = 0;
|
---|
162 | for (int i = 0; i < length; i++) {
|
---|
163 | quality += costs[i, rowAssign[i]];
|
---|
164 | }
|
---|
165 | return rowAssign;
|
---|
166 | }
|
---|
167 | }
|
---|
168 | }
|
---|