00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "algorithms/hungarian.h"
00017
00018 #include <algorithm>
00019 #include <cstdio>
00020 #include <limits>
00021
00022 namespace operations_research {
00023
00024 class HungarianOptimizer {
00025 static const int kHungarianOptimizerRowNotFound = -1;
00026 static const int kHungarianOptimizerColNotFound = -2;
00027
00028 public:
00029
00030
00031
00032
00033
00034
00035
00036
00037 explicit HungarianOptimizer(const std::vector<std::vector<double> >& costs);
00038
00039
00040
00041
00042 void Maximize(std::vector<int>* agent, std::vector<int>* task);
00043
00044
00045
00046
00047 void Minimize(std::vector<int>* agent, std::vector<int>* task);
00048 private:
00049 typedef void (HungarianOptimizer::*Step)();
00050
00051 typedef enum {
00052 NONE,
00053 PRIME,
00054 STAR
00055 } Mark;
00056
00057
00058
00059
00060 void FindAssignments(std::vector<int>* agent, std::vector<int>* task);
00061
00062
00063 bool IsStarred(int row, int col) const {
00064 return marks_[row][col] == STAR;
00065 }
00066
00067
00068 void Star(int row, int col) {
00069 marks_[row][col] = STAR;
00070 stars_in_col_[col]++;
00071 }
00072
00073
00074 void UnStar(int row, int col) {
00075 marks_[row][col] = NONE;
00076 stars_in_col_[col]--;
00077 }
00078
00079
00080
00081 int FindStarInRow(int row) const;
00082
00083
00084
00085 int FindStarInCol(int col) const;
00086
00087
00088 bool IsPrimed(int row, int col) const {
00089 return marks_[row][col] == PRIME;
00090 }
00091
00092
00093 void Prime(int row, int col) {
00094 marks_[row][col] = PRIME;
00095 }
00096
00097
00098
00099 int FindPrimeInRow(int row) const;
00100
00101
00102 void ClearPrimes();
00103
00104
00105 bool ColContainsStar(int col) const {
00106 return stars_in_col_[col] > 0;
00107 }
00108
00109
00110 bool RowCovered(int row) const {
00111 return rows_covered_[row];
00112 }
00113
00114
00115 void CoverRow(int row) {
00116 rows_covered_[row] = true;
00117 }
00118
00119
00120 void UncoverRow(int row) {
00121 rows_covered_[row] = false;
00122 }
00123
00124
00125 bool ColCovered(int col) const {
00126 return cols_covered_[col];
00127 }
00128
00129
00130 void CoverCol(int col) {
00131 cols_covered_[col] = true;
00132 }
00133
00134
00135 void UncoverCol(int col) {
00136 cols_covered_[col] = false;
00137 }
00138
00139
00140 void ClearCovers();
00141
00142
00143 double FindSmallestUncovered() const;
00144
00145
00146
00147 bool FindZero(int* zero_row, int* zero_col) const;
00148
00149
00150 void PrintMatrix();
00151
00152
00153 void DoMunkres();
00154
00155
00156
00157
00158 void ReduceRows();
00159
00160
00161
00162
00163
00164
00165 void StarZeroes();
00166
00167
00168
00169
00170
00171 void CoverStarredZeroes();
00172
00173
00174
00175
00176
00177
00178 void PrimeZeroes();
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189 void MakeAugmentingPath();
00190
00191
00192
00193
00194
00195 void AugmentPath();
00196
00197
00198 int matrix_size_;
00199
00200
00201 std::vector<std::vector<double> > costs_;
00202
00203
00204 double max_cost_;
00205
00206
00207 std::vector<bool> rows_covered_;
00208 std::vector<bool> cols_covered_;
00209
00210
00211 std::vector<std::vector<Mark> > marks_;
00212
00213
00214 std::vector<int> stars_in_col_;
00215
00216
00217 std::vector<int> preimage_;
00218 std::vector<int> image_;
00219
00220
00221 int zero_col_, zero_row_;
00222
00223
00224 int width_;
00225 int height_;
00226
00227
00228 HungarianOptimizer::Step state_;
00229 };
00230
00231 HungarianOptimizer::HungarianOptimizer(const std::vector<std::vector<double> >& costs)
00232 : matrix_size_(0),
00233 costs_(),
00234 max_cost_(0),
00235 rows_covered_(),
00236 cols_covered_(),
00237 marks_(),
00238 stars_in_col_(),
00239 preimage_(),
00240 image_(),
00241 zero_col_(0),
00242 zero_row_(0),
00243 width_(0),
00244 height_(0),
00245 state_(NULL) {
00246 width_ = costs.size();
00247
00248 if (width_ > 0) {
00249 height_ = costs[0].size();
00250 } else {
00251 height_ = 0;
00252 }
00253
00254 matrix_size_ = std::max(width_, height_);
00255 max_cost_ = 0;
00256
00257
00258
00259
00260
00261 costs_.resize(matrix_size_);
00262 for (int row = 0; row < matrix_size_; ++row) {
00263 costs_[row].resize(matrix_size_);
00264 }
00265
00266 for (int row = 0; row < matrix_size_; ++row) {
00267 for (int col = 0; col < matrix_size_; ++col) {
00268 if ((row >= width_) || (col >= height_)) {
00269 costs_[row][col] = 0;
00270 } else {
00271 costs_[row][col] = costs[row][col];
00272 max_cost_ = std::max(max_cost_, costs_[row][col]);
00273 }
00274 }
00275 }
00276
00277
00278 marks_.resize(matrix_size_);
00279 for (int row = 0; row < matrix_size_; ++row) {
00280 marks_[row].resize(matrix_size_);
00281 for (int col = 0; col < matrix_size_; ++col) {
00282 marks_[row][col]=NONE;
00283 }
00284 }
00285
00286 stars_in_col_.resize(matrix_size_);
00287
00288 rows_covered_.resize(matrix_size_);
00289 cols_covered_.resize(matrix_size_);
00290
00291 preimage_.resize(matrix_size_ * 2);
00292 image_.resize(matrix_size_ * 2);
00293 }
00294
00295
00296
00297
00298 void HungarianOptimizer::Maximize(std::vector<int>* preimage, std::vector<int>* image) {
00299
00300
00301 for (int row = 0; row < width_; ++row) {
00302 for (int col = 0; col < height_; ++col) {
00303 costs_[row][col] = max_cost_ - costs_[row][col];
00304 }
00305 }
00306 Minimize(preimage, image);
00307 }
00308
00309
00310
00311
00312
00313 void HungarianOptimizer::Minimize(std::vector<int>* preimage, std::vector<int>* image) {
00314 DoMunkres();
00315 FindAssignments(preimage, image);
00316 }
00317
00318
00319
00320
00321 void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
00322 std::vector<int>* image) {
00323 preimage->clear();
00324 image->clear();
00325 for (int row = 0; row < width_; ++row) {
00326 for (int col = 0; col < height_; ++col) {
00327 if (IsStarred(row, col)) {
00328 preimage->push_back(row);
00329 image->push_back(col);
00330 break;
00331 }
00332 }
00333 }
00334
00335
00336
00337
00338 }
00339
00340
00341
00342 int HungarianOptimizer::FindStarInRow(int row) const {
00343 for (int col = 0; col < matrix_size_; ++col) {
00344 if (IsStarred(row, col)) {
00345 return col;
00346 }
00347 }
00348
00349 return kHungarianOptimizerColNotFound;
00350 }
00351
00352
00353
00354 int HungarianOptimizer::FindStarInCol(int col) const {
00355 if (!ColContainsStar(col)) {
00356 return kHungarianOptimizerRowNotFound;
00357 }
00358
00359 for (int row = 0; row < matrix_size_; ++row) {
00360 if (IsStarred(row, col)) {
00361 return row;
00362 }
00363 }
00364
00365
00366 return kHungarianOptimizerRowNotFound;
00367 }
00368
00369
00370
00371 int HungarianOptimizer::FindPrimeInRow(int row) const {
00372 for (int col = 0; col < matrix_size_; ++col) {
00373 if (IsPrimed(row, col)) {
00374 return col;
00375 }
00376 }
00377
00378 return kHungarianOptimizerColNotFound;
00379 }
00380
00381
00382 void HungarianOptimizer::ClearPrimes() {
00383 for (int row = 0; row < matrix_size_; ++row) {
00384 for (int col = 0; col < matrix_size_; ++col) {
00385 if (IsPrimed(row, col)) {
00386 marks_[row][col] = NONE;
00387 }
00388 }
00389 }
00390 }
00391
00392
00393 void HungarianOptimizer::ClearCovers() {
00394 for (int x = 0; x < matrix_size_; x++) {
00395 UncoverRow(x);
00396 UncoverCol(x);
00397 }
00398 }
00399
00400
00401 double HungarianOptimizer::FindSmallestUncovered() const {
00402 double minval = std::numeric_limits<double>::max();
00403
00404 for (int row = 0; row < matrix_size_; ++row) {
00405 if (RowCovered(row)) {
00406 continue;
00407 }
00408
00409 for (int col = 0; col < matrix_size_; ++col) {
00410 if (ColCovered(col)) {
00411 continue;
00412 }
00413
00414 minval = std::min(minval, costs_[row][col]);
00415 }
00416 }
00417
00418 return minval;
00419 }
00420
00421
00422
00423 bool HungarianOptimizer::FindZero(int* zero_row, int* zero_col) const {
00424 for (int row = 0; row < matrix_size_; ++row) {
00425 if (RowCovered(row)) {
00426 continue;
00427 }
00428
00429 for (int col = 0; col < matrix_size_; ++col) {
00430 if (ColCovered(col)) {
00431 continue;
00432 }
00433
00434 if (costs_[row][col] == 0) {
00435 *zero_row = row;
00436 *zero_col = col;
00437 return true;
00438 }
00439 }
00440 }
00441
00442 return false;
00443 }
00444
00445
00446 void HungarianOptimizer::PrintMatrix() {
00447 for (int row = 0; row < matrix_size_; ++row) {
00448 for (int col = 0; col < matrix_size_; ++col) {
00449 printf("%g ", costs_[row][col]);
00450
00451 if (IsStarred(row, col)) {
00452 printf("*");
00453 }
00454
00455 if (IsPrimed(row, col)) {
00456 printf("'");
00457 }
00458 }
00459 printf("\n");
00460 }
00461 }
00462
00463
00464 void HungarianOptimizer::DoMunkres() {
00465 state_ = &HungarianOptimizer::ReduceRows;
00466 while (state_ != NULL) {
00467 (this->*state_)();
00468 }
00469 }
00470
00471
00472
00473
00474 void HungarianOptimizer::ReduceRows() {
00475 for (int row = 0; row < matrix_size_; ++row) {
00476 double min_cost = costs_[row][0];
00477 for (int col = 1; col < matrix_size_; ++col) {
00478 min_cost = std::min(min_cost, costs_[row][col]);
00479 }
00480 for (int col = 0; col < matrix_size_; ++col) {
00481 costs_[row][col] -= min_cost;
00482 }
00483 }
00484 state_ = &HungarianOptimizer::StarZeroes;
00485 }
00486
00487
00488
00489
00490
00491
00492 void HungarianOptimizer::StarZeroes() {
00493
00494
00495 for (int row = 0; row < matrix_size_; ++row) {
00496 if (RowCovered(row)) {
00497 continue;
00498 }
00499
00500 for (int col = 0; col < matrix_size_; ++col) {
00501 if (ColCovered(col)) {
00502 continue;
00503 }
00504
00505 if (costs_[row][col] == 0) {
00506 Star(row, col);
00507 CoverRow(row);
00508 CoverCol(col);
00509 break;
00510 }
00511 }
00512 }
00513
00514 ClearCovers();
00515 state_ = &HungarianOptimizer::CoverStarredZeroes;
00516 }
00517
00518
00519
00520
00521
00522 void HungarianOptimizer::CoverStarredZeroes() {
00523 int num_covered = 0;
00524
00525 for (int col = 0; col < matrix_size_; ++col) {
00526 if (ColContainsStar(col)) {
00527 CoverCol(col);
00528 num_covered++;
00529 }
00530 }
00531
00532 if (num_covered >= matrix_size_) {
00533 state_ = NULL;
00534 return;
00535 }
00536 state_ = &HungarianOptimizer::PrimeZeroes;
00537 }
00538
00539
00540
00541
00542
00543
00544
00545 void HungarianOptimizer::PrimeZeroes() {
00546
00547
00548
00549
00550
00551
00552 for (;;) {
00553 int zero_row, zero_col;
00554 if (!FindZero(&zero_row, &zero_col)) {
00555
00556 state_ = &HungarianOptimizer::AugmentPath;
00557 return;
00558 }
00559
00560 Prime(zero_row, zero_col);
00561 int star_col = FindStarInRow(zero_row);
00562
00563 if (star_col != kHungarianOptimizerColNotFound) {
00564 CoverRow(zero_row);
00565 UncoverCol(star_col);
00566 } else {
00567 preimage_[0] = zero_row;
00568 image_[0] = zero_col;
00569 state_ = &HungarianOptimizer::MakeAugmentingPath;
00570 return;
00571 }
00572 }
00573 }
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584 void HungarianOptimizer::MakeAugmentingPath() {
00585 bool done = false;
00586 int count = 0;
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610 while (!done) {
00611
00612 int row = FindStarInCol(image_[count]);
00613
00614 if (row != kHungarianOptimizerRowNotFound) {
00615 count++;
00616 preimage_[count] = row;
00617 image_[count] = image_[count-1];
00618 } else {
00619 done = true;
00620 }
00621
00622 if (!done) {
00623 int col = FindPrimeInRow(preimage_[count]);
00624 count++;
00625 preimage_[count] = preimage_[count-1];
00626 image_[count] = col;
00627 }
00628 }
00629
00630
00631 for (int i = 0; i <= count; ++i) {
00632 int row = preimage_[i];
00633 int col = image_[i];
00634
00635 if (IsStarred(row, col)) {
00636 UnStar(row, col);
00637 } else {
00638 Star(row, col);
00639 }
00640 }
00641
00642 ClearCovers();
00643 ClearPrimes();
00644 state_ = &HungarianOptimizer::CoverStarredZeroes;
00645 }
00646
00647
00648
00649
00650
00651 void HungarianOptimizer::AugmentPath() {
00652 double minval = FindSmallestUncovered();
00653
00654 for (int row = 0; row < matrix_size_; ++row) {
00655 for (int col = 0; col < matrix_size_; ++col) {
00656 if (RowCovered(row)) {
00657 costs_[row][col] += minval;
00658 }
00659
00660 if (!ColCovered(col)) {
00661 costs_[row][col] -= minval;
00662 }
00663 }
00664 }
00665
00666 state_ = &HungarianOptimizer::PrimeZeroes;
00667 }
00668
00669
00670 void MinimizeLinearAssignment(const std::vector<std::vector<double> >& cost,
00671 hash_map<int, int>* direct_assignment,
00672 hash_map<int, int>* reverse_assignment) {
00673 std::vector<int> agent;
00674 std::vector<int> task;
00675 HungarianOptimizer hungarian_optimizer(cost);
00676 hungarian_optimizer.Minimize(&agent, &task);
00677 for (int i = 0; i < agent.size(); ++i) {
00678 (*direct_assignment)[agent[i]] = task[i];
00679 (*reverse_assignment)[task[i]] = agent[i];
00680 }
00681 }
00682
00683 void MaximizeLinearAssignment(const std::vector<std::vector<double> >& cost,
00684 hash_map<int, int>* direct_assignment,
00685 hash_map<int, int>* reverse_assignment) {
00686 std::vector<int> agent;
00687 std::vector<int> task;
00688 HungarianOptimizer hungarian_optimizer(cost);
00689 hungarian_optimizer.Maximize(&agent, &task);
00690 for (int i = 0; i < agent.size(); ++i) {
00691 (*direct_assignment)[agent[i]] = task[i];
00692 (*reverse_assignment)[task[i]] = agent[i];
00693 }
00694 }
00695
00696 }