Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.LinearAssignment/3.3/LinearAssignmentProblemSolver.cs @ 7873

Last change on this file since 7873 was 7873, checked in by abeham, 12 years ago

#1855:

  • Added linear assignment problem (LAP)
  • Added solver (+unit test)
  • Added dependencies in HeuristicLab-3.3 project
File size: 6.1 KB
Line 
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
22using HeuristicLab.Common;
23using HeuristicLab.Data;
24
25namespace 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}
Note: See TracBrowser for help on using the repository browser.