| 1 | //Source: https://github.com/mcximing/hungarian-algorithm-cpp/blob/master/Hungarian.cpp
|
|---|
| 2 | //HungarianAlgorithm::Solve() was changed to take arrays instead of vectors as an input
|
|---|
| 3 |
|
|---|
| 4 | ///////////////////////////////////////////////////////////////////////////////
|
|---|
| 5 | // Hungarian.cpp: Implementation file for Class HungarianAlgorithm.
|
|---|
| 6 | //
|
|---|
| 7 | // This is a C++ wrapper with slight modification of a hungarian algorithm implementation by Markus Buehren.
|
|---|
| 8 | // The original implementation is a few mex-functions for use in MATLAB, found here:
|
|---|
| 9 | // http://www.mathworks.com/matlabcentral/fileexchange/6543-functions-for-the-rectangular-assignment-problem
|
|---|
| 10 | //
|
|---|
| 11 | // Both this code and the orignal code are published under the BSD license.
|
|---|
| 12 | // by Cong Ma, 2016
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 | #include <stdlib.h>
|
|---|
| 16 | #include <cfloat> // for DBL_MAX
|
|---|
| 17 | #include <math.h> // for fabs()
|
|---|
| 18 | #include "hungarian.h"
|
|---|
| 19 | #include <common/log.h>
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 | HungarianAlgorithm::HungarianAlgorithm() {}
|
|---|
| 23 | HungarianAlgorithm::~HungarianAlgorithm() {}
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 | //********************************************************//
|
|---|
| 27 | // A single function wrapper for solving assignment problem.
|
|---|
| 28 | //********************************************************//
|
|---|
| 29 | double HungarianAlgorithm::Solve(double *&distMatrixIn, int *&assignment, int nRows, int nCols)
|
|---|
| 30 | {
|
|---|
| 31 | double cost = 0.0;
|
|---|
| 32 |
|
|---|
| 33 | // call solving function
|
|---|
| 34 | assignmentoptimal(assignment, &cost, distMatrixIn, nRows, nCols);
|
|---|
| 35 |
|
|---|
| 36 | return cost;
|
|---|
| 37 | }
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 | //********************************************************//
|
|---|
| 41 | // Solve optimal solution for assignment problem using Munkres algorithm, also known as Hungarian Algorithm.
|
|---|
| 42 | //********************************************************//
|
|---|
| 43 | void HungarianAlgorithm::assignmentoptimal(int *assignment, double *cost, double *distMatrixIn, int nOfRows, int nOfColumns)
|
|---|
| 44 | {
|
|---|
| 45 | double *distMatrix, *distMatrixTemp, *distMatrixEnd, *columnEnd, value, minValue;
|
|---|
| 46 | bool *coveredColumns, *coveredRows, *starMatrix, *newStarMatrix, *primeMatrix;
|
|---|
| 47 | int nOfElements, minDim, row, col;
|
|---|
| 48 |
|
|---|
| 49 | /* initialization */
|
|---|
| 50 | *cost = 0;
|
|---|
| 51 | for (row = 0; row < nOfRows; row++)
|
|---|
| 52 | assignment[row] = -1;
|
|---|
| 53 |
|
|---|
| 54 | /* generate working copy of distance Matrix */
|
|---|
| 55 | /* check if all matrix elements are positive */
|
|---|
| 56 | nOfElements = nOfRows * nOfColumns;
|
|---|
| 57 | distMatrix = (double *)malloc(nOfElements * sizeof(double));
|
|---|
| 58 | distMatrixEnd = distMatrix + nOfElements;
|
|---|
| 59 |
|
|---|
| 60 | for (row = 0; row < nOfElements; row++)
|
|---|
| 61 | {
|
|---|
| 62 | value = distMatrixIn[row];
|
|---|
| 63 | if (value < 0)
|
|---|
| 64 | logPrintf("HungarianAlgorithm", "assignmentoptimal", LOG_ERROR, "All matrix elements have to be non-negative, not %g", value);
|
|---|
| 65 | distMatrix[row] = value;
|
|---|
| 66 | }
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 | /* memory allocation */
|
|---|
| 70 | coveredColumns = (bool *)calloc(nOfColumns, sizeof(bool));
|
|---|
| 71 | coveredRows = (bool *)calloc(nOfRows, sizeof(bool));
|
|---|
| 72 | starMatrix = (bool *)calloc(nOfElements, sizeof(bool));
|
|---|
| 73 | primeMatrix = (bool *)calloc(nOfElements, sizeof(bool));
|
|---|
| 74 | newStarMatrix = (bool *)calloc(nOfElements, sizeof(bool)); /* used in step4 */
|
|---|
| 75 |
|
|---|
| 76 | /* preliminary steps */
|
|---|
| 77 | if (nOfRows <= nOfColumns)
|
|---|
| 78 | {
|
|---|
| 79 | minDim = nOfRows;
|
|---|
| 80 |
|
|---|
| 81 | for (row = 0; row < nOfRows; row++)
|
|---|
| 82 | {
|
|---|
| 83 | /* find the smallest element in the row */
|
|---|
| 84 | distMatrixTemp = distMatrix + row;
|
|---|
| 85 | minValue = *distMatrixTemp;
|
|---|
| 86 | distMatrixTemp += nOfRows;
|
|---|
| 87 | while (distMatrixTemp < distMatrixEnd)
|
|---|
| 88 | {
|
|---|
| 89 | value = *distMatrixTemp;
|
|---|
| 90 | if (value < minValue)
|
|---|
| 91 | minValue = value;
|
|---|
| 92 | distMatrixTemp += nOfRows;
|
|---|
| 93 | }
|
|---|
| 94 |
|
|---|
| 95 | /* subtract the smallest element from each element of the row */
|
|---|
| 96 | distMatrixTemp = distMatrix + row;
|
|---|
| 97 | while (distMatrixTemp < distMatrixEnd)
|
|---|
| 98 | {
|
|---|
| 99 | *distMatrixTemp -= minValue;
|
|---|
| 100 | distMatrixTemp += nOfRows;
|
|---|
| 101 | }
|
|---|
| 102 | }
|
|---|
| 103 |
|
|---|
| 104 | /* Steps 1 and 2a */
|
|---|
| 105 | for (row = 0; row < nOfRows; row++)
|
|---|
| 106 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 107 | if (fabs(distMatrix[row + nOfRows * col]) < DBL_EPSILON)
|
|---|
| 108 | if (!coveredColumns[col])
|
|---|
| 109 | {
|
|---|
| 110 | starMatrix[row + nOfRows * col] = true;
|
|---|
| 111 | coveredColumns[col] = true;
|
|---|
| 112 | break;
|
|---|
| 113 | }
|
|---|
| 114 | }
|
|---|
| 115 | else /* if(nOfRows > nOfColumns) */
|
|---|
| 116 | {
|
|---|
| 117 | minDim = nOfColumns;
|
|---|
| 118 |
|
|---|
| 119 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 120 | {
|
|---|
| 121 | /* find the smallest element in the column */
|
|---|
| 122 | distMatrixTemp = distMatrix + nOfRows * col;
|
|---|
| 123 | columnEnd = distMatrixTemp + nOfRows;
|
|---|
| 124 |
|
|---|
| 125 | minValue = *distMatrixTemp++;
|
|---|
| 126 | while (distMatrixTemp < columnEnd)
|
|---|
| 127 | {
|
|---|
| 128 | value = *distMatrixTemp++;
|
|---|
| 129 | if (value < minValue)
|
|---|
| 130 | minValue = value;
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | /* subtract the smallest element from each element of the column */
|
|---|
| 134 | distMatrixTemp = distMatrix + nOfRows * col;
|
|---|
| 135 | while (distMatrixTemp < columnEnd)
|
|---|
| 136 | *distMatrixTemp++ -= minValue;
|
|---|
| 137 | }
|
|---|
| 138 |
|
|---|
| 139 | /* Steps 1 and 2a */
|
|---|
| 140 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 141 | for (row = 0; row < nOfRows; row++)
|
|---|
| 142 | if (fabs(distMatrix[row + nOfRows * col]) < DBL_EPSILON)
|
|---|
| 143 | if (!coveredRows[row])
|
|---|
| 144 | {
|
|---|
| 145 | starMatrix[row + nOfRows * col] = true;
|
|---|
| 146 | coveredColumns[col] = true;
|
|---|
| 147 | coveredRows[row] = true;
|
|---|
| 148 | break;
|
|---|
| 149 | }
|
|---|
| 150 | for (row = 0; row < nOfRows; row++)
|
|---|
| 151 | coveredRows[row] = false;
|
|---|
| 152 |
|
|---|
| 153 | }
|
|---|
| 154 |
|
|---|
| 155 | /* move to step 2b */
|
|---|
| 156 | step2b(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim);
|
|---|
| 157 |
|
|---|
| 158 | /* compute cost and remove invalid assignments */
|
|---|
| 159 | computeassignmentcost(assignment, cost, distMatrixIn, nOfRows);
|
|---|
| 160 |
|
|---|
| 161 | /* free allocated memory */
|
|---|
| 162 | free(distMatrix);
|
|---|
| 163 | free(coveredColumns);
|
|---|
| 164 | free(coveredRows);
|
|---|
| 165 | free(starMatrix);
|
|---|
| 166 | free(primeMatrix);
|
|---|
| 167 | free(newStarMatrix);
|
|---|
| 168 |
|
|---|
| 169 | return;
|
|---|
| 170 | }
|
|---|
| 171 |
|
|---|
| 172 | /********************************************************/
|
|---|
| 173 | void HungarianAlgorithm::buildassignmentvector(int *assignment, bool *starMatrix, int nOfRows, int nOfColumns)
|
|---|
| 174 | {
|
|---|
| 175 | int row, col;
|
|---|
| 176 |
|
|---|
| 177 | for (row = 0; row < nOfRows; row++)
|
|---|
| 178 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 179 | if (starMatrix[row + nOfRows * col])
|
|---|
| 180 | {
|
|---|
| 181 | #ifdef ONE_INDEXING
|
|---|
| 182 | assignment[row] = col + 1; /* MATLAB-Indexing */
|
|---|
| 183 | #else
|
|---|
| 184 | assignment[row] = col;
|
|---|
| 185 | #endif
|
|---|
| 186 | break;
|
|---|
| 187 | }
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | /********************************************************/
|
|---|
| 191 | void HungarianAlgorithm::computeassignmentcost(int *assignment, double *cost, double *distMatrix, int nOfRows)
|
|---|
| 192 | {
|
|---|
| 193 | int row, col;
|
|---|
| 194 |
|
|---|
| 195 | for (row = 0; row < nOfRows; row++)
|
|---|
| 196 | {
|
|---|
| 197 | col = assignment[row];
|
|---|
| 198 | if (col >= 0)
|
|---|
| 199 | *cost += distMatrix[row + nOfRows * col];
|
|---|
| 200 | }
|
|---|
| 201 | }
|
|---|
| 202 |
|
|---|
| 203 | /********************************************************/
|
|---|
| 204 | void HungarianAlgorithm::step2a(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim)
|
|---|
| 205 | {
|
|---|
| 206 | bool *starMatrixTemp, *columnEnd;
|
|---|
| 207 | int col;
|
|---|
| 208 |
|
|---|
| 209 | /* cover every column containing a starred zero */
|
|---|
| 210 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 211 | {
|
|---|
| 212 | starMatrixTemp = starMatrix + nOfRows * col;
|
|---|
| 213 | columnEnd = starMatrixTemp + nOfRows;
|
|---|
| 214 | while (starMatrixTemp < columnEnd) {
|
|---|
| 215 | if (*starMatrixTemp++)
|
|---|
| 216 | {
|
|---|
| 217 | coveredColumns[col] = true;
|
|---|
| 218 | break;
|
|---|
| 219 | }
|
|---|
| 220 | }
|
|---|
| 221 | }
|
|---|
| 222 |
|
|---|
| 223 | /* move to step 3 */
|
|---|
| 224 | step2b(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim);
|
|---|
| 225 | }
|
|---|
| 226 |
|
|---|
| 227 | /********************************************************/
|
|---|
| 228 | void HungarianAlgorithm::step2b(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim)
|
|---|
| 229 | {
|
|---|
| 230 | int col, nOfCoveredColumns;
|
|---|
| 231 |
|
|---|
| 232 | /* count covered columns */
|
|---|
| 233 | nOfCoveredColumns = 0;
|
|---|
| 234 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 235 | if (coveredColumns[col])
|
|---|
| 236 | nOfCoveredColumns++;
|
|---|
| 237 |
|
|---|
| 238 | if (nOfCoveredColumns == minDim)
|
|---|
| 239 | {
|
|---|
| 240 | /* algorithm finished */
|
|---|
| 241 | buildassignmentvector(assignment, starMatrix, nOfRows, nOfColumns);
|
|---|
| 242 | }
|
|---|
| 243 | else
|
|---|
| 244 | {
|
|---|
| 245 | /* move to step 3 */
|
|---|
| 246 | step3(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim);
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | }
|
|---|
| 250 |
|
|---|
| 251 | /********************************************************/
|
|---|
| 252 | void HungarianAlgorithm::step3(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim)
|
|---|
| 253 | {
|
|---|
| 254 | bool zerosFound;
|
|---|
| 255 | int row, col, starCol;
|
|---|
| 256 |
|
|---|
| 257 | zerosFound = true;
|
|---|
| 258 | while (zerosFound)
|
|---|
| 259 | {
|
|---|
| 260 | zerosFound = false;
|
|---|
| 261 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 262 | if (!coveredColumns[col])
|
|---|
| 263 | for (row = 0; row < nOfRows; row++)
|
|---|
| 264 | if ((!coveredRows[row]) && (fabs(distMatrix[row + nOfRows * col]) < DBL_EPSILON))
|
|---|
| 265 | {
|
|---|
| 266 | /* prime zero */
|
|---|
| 267 | primeMatrix[row + nOfRows * col] = true;
|
|---|
| 268 |
|
|---|
| 269 | /* find starred zero in current row */
|
|---|
| 270 | for (starCol = 0; starCol < nOfColumns; starCol++)
|
|---|
| 271 | if (starMatrix[row + nOfRows * starCol])
|
|---|
| 272 | break;
|
|---|
| 273 |
|
|---|
| 274 | if (starCol == nOfColumns) /* no starred zero found */
|
|---|
| 275 | {
|
|---|
| 276 | /* move to step 4 */
|
|---|
| 277 | step4(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim, row, col);
|
|---|
| 278 | return;
|
|---|
| 279 | }
|
|---|
| 280 | else
|
|---|
| 281 | {
|
|---|
| 282 | coveredRows[row] = true;
|
|---|
| 283 | coveredColumns[starCol] = false;
|
|---|
| 284 | zerosFound = true;
|
|---|
| 285 | break;
|
|---|
| 286 | }
|
|---|
| 287 | }
|
|---|
| 288 | }
|
|---|
| 289 |
|
|---|
| 290 | /* move to step 5 */
|
|---|
| 291 | step5(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim);
|
|---|
| 292 | }
|
|---|
| 293 |
|
|---|
| 294 | /********************************************************/
|
|---|
| 295 | void HungarianAlgorithm::step4(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim, int row, int col)
|
|---|
| 296 | {
|
|---|
| 297 | int n, starRow, starCol, primeRow, primeCol;
|
|---|
| 298 | int nOfElements = nOfRows * nOfColumns;
|
|---|
| 299 |
|
|---|
| 300 | /* generate temporary copy of starMatrix */
|
|---|
| 301 | for (n = 0; n < nOfElements; n++)
|
|---|
| 302 | newStarMatrix[n] = starMatrix[n];
|
|---|
| 303 |
|
|---|
| 304 | /* star current zero */
|
|---|
| 305 | newStarMatrix[row + nOfRows * col] = true;
|
|---|
| 306 |
|
|---|
| 307 | /* find starred zero in current column */
|
|---|
| 308 | starCol = col;
|
|---|
| 309 | for (starRow = 0; starRow < nOfRows; starRow++)
|
|---|
| 310 | if (starMatrix[starRow + nOfRows * starCol])
|
|---|
| 311 | break;
|
|---|
| 312 |
|
|---|
| 313 | while (starRow < nOfRows)
|
|---|
| 314 | {
|
|---|
| 315 | /* unstar the starred zero */
|
|---|
| 316 | newStarMatrix[starRow + nOfRows * starCol] = false;
|
|---|
| 317 |
|
|---|
| 318 | /* find primed zero in current row */
|
|---|
| 319 | primeRow = starRow;
|
|---|
| 320 | for (primeCol = 0; primeCol < nOfColumns; primeCol++)
|
|---|
| 321 | if (primeMatrix[primeRow + nOfRows * primeCol])
|
|---|
| 322 | break;
|
|---|
| 323 |
|
|---|
| 324 | /* star the primed zero */
|
|---|
| 325 | newStarMatrix[primeRow + nOfRows * primeCol] = true;
|
|---|
| 326 |
|
|---|
| 327 | /* find starred zero in current column */
|
|---|
| 328 | starCol = primeCol;
|
|---|
| 329 | for (starRow = 0; starRow < nOfRows; starRow++)
|
|---|
| 330 | if (starMatrix[starRow + nOfRows * starCol])
|
|---|
| 331 | break;
|
|---|
| 332 | }
|
|---|
| 333 |
|
|---|
| 334 | /* use temporary copy as new starMatrix */
|
|---|
| 335 | /* delete all primes, uncover all rows */
|
|---|
| 336 | for (n = 0; n < nOfElements; n++)
|
|---|
| 337 | {
|
|---|
| 338 | primeMatrix[n] = false;
|
|---|
| 339 | starMatrix[n] = newStarMatrix[n];
|
|---|
| 340 | }
|
|---|
| 341 | for (n = 0; n < nOfRows; n++)
|
|---|
| 342 | coveredRows[n] = false;
|
|---|
| 343 |
|
|---|
| 344 | /* move to step 2a */
|
|---|
| 345 | step2a(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim);
|
|---|
| 346 | }
|
|---|
| 347 |
|
|---|
| 348 | /********************************************************/
|
|---|
| 349 | void HungarianAlgorithm::step5(int *assignment, double *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, int nOfRows, int nOfColumns, int minDim)
|
|---|
| 350 | {
|
|---|
| 351 | double h, value;
|
|---|
| 352 | int row, col;
|
|---|
| 353 |
|
|---|
| 354 | /* find smallest uncovered element h */
|
|---|
| 355 | h = DBL_MAX;
|
|---|
| 356 | for (row = 0; row < nOfRows; row++)
|
|---|
| 357 | if (!coveredRows[row])
|
|---|
| 358 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 359 | if (!coveredColumns[col])
|
|---|
| 360 | {
|
|---|
| 361 | value = distMatrix[row + nOfRows * col];
|
|---|
| 362 | if (value < h)
|
|---|
| 363 | h = value;
|
|---|
| 364 | }
|
|---|
| 365 |
|
|---|
| 366 | /* add h to each covered row */
|
|---|
| 367 | for (row = 0; row < nOfRows; row++)
|
|---|
| 368 | if (coveredRows[row])
|
|---|
| 369 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 370 | distMatrix[row + nOfRows * col] += h;
|
|---|
| 371 |
|
|---|
| 372 | /* subtract h from each uncovered column */
|
|---|
| 373 | for (col = 0; col < nOfColumns; col++)
|
|---|
| 374 | if (!coveredColumns[col])
|
|---|
| 375 | for (row = 0; row < nOfRows; row++)
|
|---|
| 376 | distMatrix[row + nOfRows * col] -= h;
|
|---|
| 377 |
|
|---|
| 378 | /* move to step 3 */
|
|---|
| 379 | step3(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim);
|
|---|
| 380 | }
|
|---|