├── AuctionAlgorithm.h ├── CMakeLists.txt ├── LICENSE ├── Miller.h ├── README.md └── main.cpp /AuctionAlgorithm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AuctionAlgorithm.h 3 | * 4 | * Created on: 30.07.2013 5 | * Author: fb 6 | */ 7 | 8 | #ifndef AUCTIONALGORITHM_H_ 9 | #define AUCTIONALGORITHM_H_ 10 | 11 | #include "eigen/Eigen/Core" 12 | #include 13 | 14 | #define __AUCTION_EPSILON_MULTIPLIER 1e-5 15 | #define __AUCTION_INF 1e8 16 | //#define __AUCTION_OMIT_ZEROS 17 | #define __AUCTION_ZERO 0. 18 | 19 | template 20 | class Auction 21 | { 22 | private: 23 | 24 | Auction() = delete; 25 | 26 | virtual ~Auction(){} 27 | public: 28 | 29 | typedef Eigen::Matrix WeightMatrix; 30 | 31 | /** 32 | * solver modes for association problem 33 | */ 34 | enum APSolvingMode 35 | { 36 | MAXIMIZATION = 1, MINIMIZATION = 2 37 | }; 38 | 39 | /** 40 | * represents an undirected edge between node x and y with weight v 41 | */ 42 | struct Edge 43 | { 44 | public: 45 | Edge() : x(0), y(0), v(0) {} 46 | Edge(const size_t x, const size_t y, const Scalar v) : 47 | x(x), y(y), v(v) {} 48 | 49 | size_t x; 50 | size_t y; 51 | Scalar v; 52 | }; 53 | 54 | 55 | /** 56 | * vector of edges 57 | */ 58 | typedef std::vector Edges; 59 | 60 | 61 | /** 62 | * vector of scalars (prices, profits, ...) 63 | */ 64 | typedef std::vector Scalars; 65 | 66 | /** 67 | * vector of bools for row/column-locking 68 | */ 69 | typedef std::vector Locks; 70 | 71 | /** 72 | * vector of indices 73 | */ 74 | typedef std::vector indices; 75 | 76 | static const Edges solve(const Eigen::Matrix & a) 77 | { 78 | const size_t rows = a.rows(); 79 | const size_t cols = a.cols(); 80 | 81 | Locks lockedRows(a.rows(), false); 82 | Locks lockedCols(a.cols(), false); 83 | Edges E; 84 | 85 | Scalar lambda = .0; 86 | Scalar epsilon = __AUCTION_EPSILON_MULTIPLIER / a.cols(); 87 | 88 | // condition 3: initially set p_j >= lambda 89 | Scalars prices(cols, 0.), profits(rows, 1.); // p-Vector (1 to j) = p_j 90 | 91 | do 92 | { 93 | // Step 1 (forward auction cycle): 94 | // Execute iterations of the forward auction algorithm until at least one 95 | // more person becomes assigned. If there is an unassigned person left, go 96 | // to step 2; else go to step 3. 97 | while (forward(a, E, prices, profits, lockedRows, lockedCols, 98 | lambda, epsilon)) 99 | ; 100 | 101 | if (!allPersonsAssigned(lockedRows)) 102 | { 103 | 104 | // Step 2 (reverse auction cycle): 105 | // Execute several iterations of the reverse auction algorithm until at least 106 | // one more object becomes assigned or until we have p_j <= lambda for all 107 | // unassigned objects. If there is an unassigned person left, go to step 1 108 | // else go to step 3 109 | while (!reverse(a, E, prices, profits, lockedRows, lockedCols, 110 | lambda, epsilon) 111 | || !unassignedObjectsLTlambda(lockedCols, prices, 112 | lambda)) 113 | ; // reverse auction 114 | } 115 | 116 | if (allPersonsAssigned(lockedRows)) 117 | { 118 | // Step 3 (reverse auction): 119 | // Execute successive iterations of the reverse auction algorithm until the 120 | // algorithm terminates with p_j <= lambda for all unassigned objects j 121 | while (true) 122 | { 123 | reverse(a, E, prices, profits, lockedRows, lockedCols, 124 | lambda, epsilon); 125 | if (unassignedObjectsLTlambda(lockedCols, prices, lambda)) 126 | break; 127 | } 128 | break; 129 | } 130 | 131 | } while (true); 132 | 133 | return E; 134 | } 135 | private: 136 | 137 | /** 138 | * forward cycle of auction algorithm 139 | * @param a weight matrix (nxm) 140 | * @param S assignment matrix (nxm) 141 | * @param prices prices per object (m) 142 | * @param profits profits per person (n) 143 | * @param lambda bidding threshold lambda 144 | * @param epsilon bidding increment 145 | * @return true if assignment was made, false otherwise 146 | */ 147 | static bool forward(const Eigen::Matrix & a, Edges & E, 148 | Scalars & prices, Scalars & profits, Locks & lockedRows, 149 | Locks & lockedCols, Scalar & lambda, Scalar & epsilon) 150 | { 151 | #ifdef __AUCTION_DEBUG 152 | __A_FORWARD_LOG << "forwarding ..." << std::endl; 153 | #endif 154 | const size_t rows = a.rows(); 155 | const size_t cols = a.cols(); 156 | bool assignmentFound = false; 157 | 158 | for (size_t i = 0; i < rows; i++) // for the i-th row/person 159 | { 160 | #ifdef __AUCTION_DEBUG 161 | __A_FORWARD_LOG << "examining row " << i << std::endl; 162 | #endif 163 | bool assignmentInThisIterationFound = false; 164 | 165 | // person already assigned? 166 | if (lockedRows[i]) 167 | continue; 168 | 169 | #ifdef __AUCTION_DEBUG 170 | __A_FORWARD_LOG << "row " << i << " not locked!" << std::endl; 171 | #endif 172 | // find an unassigned person i, its best object j_i 173 | // j_i = argmax {a_ij - p_j} for j in A(i) ( A(i) are the set of edges of the i-th row ) 174 | // if a(i,j) = 0. it is not a valid edge 175 | size_t j_i = 0; 176 | 177 | // v_i = max { a_ij - p_j} for j in A(i) // maximum profit for person i 178 | // v_i was already found = v_i 179 | // w_i = max { a_ij - p_j} for j in A(i) and j != j_i // second best profit 180 | // if j_i is the only entry in A(i), w_i = - inf // there's no second best profit 181 | Scalar w_i = -__AUCTION_INF, v_i = -__AUCTION_INF, a_i_ji = 0.; // = max { a_ij - p_j} 182 | 183 | // find maximum profit i.e. j_i = arg max { a_ij - p_j} and second best 184 | for (size_t j = 0; j < cols; j++) // for the j-th column 185 | { 186 | const Scalar aij = a(i,j); 187 | #ifndef __AUCTION_OMIT_ZEROS 188 | if ( aij == __AUCTION_ZERO ) continue; 189 | #endif 190 | const Scalar diff = aij - prices[j]; 191 | #ifdef __AUCTION_DEBUG 192 | __A_FORWARD_LOG << " col " << j << " diff = " << diff << std::endl; 193 | #endif 194 | if (diff > v_i) 195 | { 196 | #ifdef __AUCTION_DEBUG 197 | __A_FORWARD_LOG << " diff > v_i !" << std::endl; 198 | #endif 199 | // if there already was an entry found, this is the second best 200 | if (assignmentInThisIterationFound) 201 | w_i = v_i; 202 | 203 | v_i = diff; 204 | j_i = j; 205 | a_i_ji = aij; 206 | assignmentInThisIterationFound = true; 207 | } 208 | if (diff > w_i && j_i != j) 209 | w_i = diff; 210 | // if no entry is bigger than v_i, check if there's still a bigger second best entry 211 | } 212 | 213 | // no possible assignment found? 214 | if (!assignmentInThisIterationFound) 215 | { 216 | lockedRows[i] = true; // if no assignment found in this row, there is no arc ... 217 | continue; 218 | } 219 | #ifdef __AUCTION_DEBUG 220 | __A_FORWARD_LOG << "i = " << i << " - j_i = " << j_i << " - v_i = " << v_i 221 | << " - w_i = " << w_i << " - a_ij = " << a_i_ji << std::endl; 222 | #endif 223 | assignmentInThisIterationFound = false; 224 | 225 | // std::cout << "assignment found .." << std::endl; 226 | const Scalar bid = a_i_ji - w_i + epsilon; 227 | 228 | // P_i = w_i - E 229 | profits[i] = w_i - epsilon; // set new profit for person 230 | 231 | // prices(j_i) = max(lambda, a(i,j_i) - w(i) + epsilon) 232 | // if lambda <= a_ij - w_i + E, add (i, j_i) to S 233 | if (lambda <= bid) 234 | { 235 | prices[j_i] = bid; 236 | // assignment was made, so lock row and col 237 | lockedRows[i] = true; 238 | lockedCols[j_i] = true; 239 | 240 | bool newEdge = true; 241 | 242 | // if j_i was assigned to different i' to begin, remove (i', j_i) from S 243 | for (auto & e : E) 244 | if (e.y == j_i) // change edge 245 | { 246 | lockedRows[e.x] = false; // unlock row i' 247 | newEdge = false; 248 | e.x = i; 249 | e.v = a_i_ji; 250 | break; 251 | } 252 | if (newEdge) 253 | { 254 | Edge e; 255 | e.x = i; 256 | e.y = j_i; 257 | e.v = a_i_ji; 258 | E.push_back(e); 259 | #ifdef __AUCTION_DEBUG 260 | __A_FORWARD_LOG << "adding edge (" << i << ", " << j_i << ")" << std::endl; 261 | #endif 262 | } 263 | assignmentInThisIterationFound = true; 264 | 265 | } 266 | else 267 | { 268 | prices[j_i] = lambda; 269 | assignmentInThisIterationFound = false; 270 | 271 | } 272 | if (assignmentInThisIterationFound) 273 | assignmentFound = true; 274 | } 275 | return assignmentFound; 276 | 277 | } 278 | 279 | /** 280 | * reverse cycle of auction algorithm 281 | * @param a weight matrix (nxm) 282 | * @param S assignment matrix (nxm) 283 | * @param prices prices per object (m) 284 | * @param profits profits per person (n) 285 | * @param lambda bidding threshold lambda 286 | * @param epsilon bidding increment 287 | * @return true if assignment was made, false otherwise 288 | */ 289 | static bool reverse(const Eigen::Matrix & a, Edges & E, 290 | Scalars & prices, Scalars & profits, Locks & lockedRows, 291 | Locks & lockedCols, Scalar & lambda, const Scalar & epsilon) 292 | { 293 | #ifdef __AUCTION_DEBUG 294 | __A_REVERSE_LOG << "reversing ..." << std::endl; 295 | #endif 296 | const size_t rows = a.rows(); 297 | const size_t cols = a.cols(); 298 | 299 | bool assignmentFound = false; 300 | 301 | for (size_t j = 0; j < cols; j++) // for the j-th column (objects) 302 | { 303 | bool assignmentInThisIterationFound = false; 304 | 305 | // object already assigned, p_j > lambda ? 306 | if (lockedCols[j]) 307 | continue; 308 | 309 | if (!(prices[j] > lambda)) 310 | continue; 311 | 312 | // Find an unassigned object j with p_j > lambda, its best person i_j 313 | // i_j = argmax {a_ij - profits[i]) für i aus B(j) (PI !!!) 314 | size_t i_j = 0; 315 | 316 | //g_j = max {a_ij - P_i} for i in B(j) and i != i_j 317 | // if j_i is the only entry in B(j), g_j = - inf ( g_j < b_j) 318 | //b_j = max {a_ij - P_i} for i in B(j) 319 | Scalar b_j = -__AUCTION_INF, g_j = -__AUCTION_INF, a_ij_j = 0.; 320 | 321 | // find maximum profit i.e. j_i = arg max { a_ij - p_j} and second best 322 | for (size_t i = 0; i < rows; i++) // for the j-th column 323 | { 324 | const Scalar aij = a(i, j); 325 | #ifndef __AUCTION_OMIT_ZEROS 326 | if ( aij == __AUCTION_ZERO ) continue; 327 | #endif 328 | const Scalar diff = aij - profits[i]; 329 | if (diff > b_j) 330 | { 331 | // if there already was an entry found, this is the second best 332 | if (assignmentInThisIterationFound) 333 | g_j = b_j; 334 | 335 | b_j = diff; 336 | i_j = i; 337 | a_ij_j = aij; 338 | assignmentInThisIterationFound = true; 339 | } 340 | if (diff > g_j && i_j != i) 341 | g_j = diff; 342 | } 343 | 344 | // no assignment found 345 | if (!assignmentInThisIterationFound) 346 | { 347 | lockedCols[j] = true; 348 | continue; 349 | } 350 | #ifdef __AUCTION_DEBUG 351 | __A_REVERSE_LOG << "j = " << j << " i_j = " << i_j << " b_j = " << b_j << " g_j = " << g_j 352 | << " a_ij_j = " << a_ij_j 353 | << " p_j = " << prices[j] << " P_i = " << profits[i_j]<< std::endl; 354 | #endif 355 | assignmentInThisIterationFound = false; 356 | 357 | //if b_j >= L + E, case 1: 358 | if (b_j >= (lambda + epsilon)) 359 | { 360 | #ifdef __AUCTION_DEBUG 361 | __A_REVERSE_LOG << "b_j >= lambda + epsilon" << std::endl; 362 | #endif 363 | const Scalar diff = g_j - epsilon; // G_j - E 364 | 365 | const Scalar max = lambda > diff ? lambda : diff; // max { L, G_j - E} 366 | 367 | // p_j = max { L, G_j - E} 368 | prices[j] = max; 369 | 370 | // P_i_j = a_i_jj - max {L, G_j - E} 371 | profits[i_j] = a_ij_j - max; 372 | 373 | lockedRows[i_j] = true; 374 | lockedCols[j] = true; 375 | 376 | bool newEdge = true; 377 | 378 | // if j_i was assigned to different i' to begin, remove (i', j_i) from S 379 | for (auto & e : E) 380 | if (e.x == i_j) // change edge 381 | { 382 | lockedCols[e.y] = false; // unlock row i' 383 | newEdge = false; 384 | e.y = j; 385 | e.v = a_ij_j; 386 | #ifdef __AUCTION_DEBUG 387 | __A_REVERSE_LOG << "edges: " << E.size() 388 | << " changing edge "; 389 | #endif 390 | break; 391 | 392 | } 393 | if (newEdge) 394 | { 395 | Edge e; 396 | e.x = i_j; 397 | e.y = j; 398 | e.v = a_ij_j; 399 | E.push_back(e); 400 | #ifdef __AUCTION_DEBUG 401 | __A_REVERSE_LOG << "added edge " << E.size() << " "; 402 | #endif 403 | } 404 | assignmentInThisIterationFound = true; 405 | } 406 | else // if B_j < L + E, case 2 407 | { 408 | // p_j = B_j - E 409 | prices[j] = b_j - epsilon; 410 | #ifdef __AUCTION_DEBUG 411 | __A_REVERSE_LOG << "b_j < lambda + epsilon " << std::endl; 412 | #endif 413 | /** standard lambda scaling **/ 414 | size_t lowerThanLambda = 0; 415 | Scalar newLambda = lambda; 416 | 417 | // if the number of objectes k with p_k < lambda is bigger than (rows - cols) 418 | for (size_t k = 0; k < cols; k++) 419 | { 420 | if (prices[k] < lambda) // p_k < lambda 421 | { 422 | lowerThanLambda++; 423 | if (prices[k] < newLambda) 424 | newLambda = prices[k]; 425 | } 426 | } 427 | // set new lambda 428 | #ifdef __AUCTION_DEBUG 429 | __A_REVERSE_LOG << "changing lambda from " << lambda << " to " << newLambda << std::endl; 430 | #endif 431 | if (lowerThanLambda >= (cols - rows)) 432 | lambda = newLambda; 433 | assignmentInThisIterationFound = false; 434 | } 435 | if (assignmentInThisIterationFound) 436 | assignmentFound = true; 437 | } 438 | return assignmentFound; 439 | } 440 | 441 | 442 | /** 443 | * returns true if p_j <= lambda for all unassigned objects. 444 | * 445 | * @param c locked columns 446 | * @param prices prices of objects 447 | * @param lambda bidding threshold 448 | * @return true if all prices of unassigned objects are below lambda, otherwise false 449 | */ 450 | static const bool unassignedObjectsLTlambda(const Locks & c, 451 | const Scalars & prices, const Scalar lambda) 452 | { 453 | for (size_t j = 0; j < c.size(); ++j) 454 | if (!c[j] && prices[j] > lambda) 455 | return false; 456 | 457 | return true; 458 | } 459 | 460 | 461 | /** 462 | * check if all persons are assigned 463 | * @return true if all persons are assigned, otherwise false 464 | */ 465 | static const bool allPersonsAssigned(const Locks & r) 466 | { 467 | for (size_t i = 0; i < r.size(); ++i) 468 | if (!r[i]) 469 | return false; 470 | return true; 471 | } 472 | }; 473 | 474 | 475 | #endif /* AUCTIONALGORITHM_H_ */ 476 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(MurtyAlgorithm) 2 | cmake_minimum_required(VERSION 2.8) 3 | aux_source_directory(. SRC_LIST) 4 | add_executable(${PROJECT_NAME} ${SRC_LIST}) 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Florian Bäuerlein 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Miller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MurtyMiller.h 3 | * 4 | * Created on: 15.08.2013 5 | * Author: fb 6 | * 7 | * Murty's algorithm implementation according to 8 | * Miller's pseudo-code formulation in "Optimizing Murty's ranked assignment method" 9 | * 10 | * Miller, M.L.; Stone, H.S.; Cox, Ingemar J., "Optimizing Murty's ranked assignment method," 11 | * Aerospace and Electronic Systems, IEEE Transactions on , vol.33, no.3, pp.851,862, July 1997 12 | * doi: 10.1109/7.599256 13 | */ 14 | 15 | #ifndef MILLER_H_ 16 | #define MILLER_H_ 17 | 18 | #include "AuctionAlgorithm.h" 19 | #include 20 | 21 | 22 | template 23 | class MurtyMiller 24 | { 25 | public: 26 | 27 | typedef Eigen::Matrix WeightMatrix; 28 | typedef Eigen::Matrix AssignmentMatrix; 29 | typedef typename Auction::Edge Edge; 30 | typedef typename Auction::Edges Edges; 31 | 32 | /** 33 | * a partition represents an assignment matrix (i.e. edges) 34 | * with it's weight matrix 35 | * see Murty's algorithm for details 36 | */ 37 | class Partition 38 | { 39 | public: 40 | Partition() : value(0) 41 | { 42 | w = WeightMatrix::Zero(w.rows(), w.cols()); 43 | } 44 | 45 | Partition(const Edges & edges, const WeightMatrix & w, const Scalar v) : 46 | edges(edges), w(w), value(v) 47 | {} 48 | 49 | Edges edges; 50 | WeightMatrix w; 51 | Scalar value; 52 | }; 53 | 54 | struct ComparePartition: std::binary_function 55 | { 56 | bool operator()(const Partition & lhs, const Partition & rhs) const 57 | { 58 | return ( lhs.value < rhs.value ); 59 | } 60 | }; 61 | 62 | /** 63 | * list of partitions 64 | */ 65 | typedef typename std::vector Partitions; 66 | 67 | /** 68 | * sum up values of edges, i.e. objective function value 69 | * @param edges 70 | * @return 71 | */ 72 | static Scalar objectiveFunctionValue(const Edges & edges ) 73 | { 74 | Scalar v = 0; 75 | for ( const auto & e : edges ) 76 | v += e.v; 77 | 78 | return v; 79 | } 80 | 81 | static typename std::vector getMBestAssignments(const WeightMatrix & w, const size_t mBest = 5 ) 82 | { 83 | const size_t rows = w.rows(), cols = w.cols(); 84 | 85 | assert( rows != 0 && cols != 0 && cols >= rows ); 86 | 87 | typename std::vector resultingEdges; 88 | 89 | // special case if rows = cols = 1 90 | if ( cols == 1 && rows == 1 ) 91 | { 92 | if (w(0, 0) == 0 ) return resultingEdges; 93 | 94 | Edges edges; 95 | edges.emplace_back(Edge(0, 0, w(0, 0))); 96 | resultingEdges.emplace_back(edges); 97 | return resultingEdges; 98 | } 99 | 100 | size_t kBest = 0; 101 | 102 | const size_t maxComb = ( rows > cols ) ? rows : cols; 103 | // if rows! < mBest ... 104 | switch (maxComb) 105 | { 106 | case 1 : kBest = 1; break; 107 | case 2 : kBest = 2; break; 108 | case 3 : kBest = 6; break; 109 | case 4 : kBest = 24; break; 110 | default: kBest = mBest; break; 111 | } 112 | if ( mBest < kBest ) kBest = mBest; 113 | 114 | std::cout << "kBest = " << kBest << std::endl; 115 | 116 | Edges edges = Auction::solve(w); // make initial (best) assignment 117 | 118 | // sort edges by row 119 | std::sort(edges.begin(), edges.end(), [](const Edge & e1, const Edge & e2) {return e1.x < e2.x;}); 120 | 121 | // initial partition, i.e. best solution 122 | Partition init(edges, w, objectiveFunctionValue(edges)); 123 | 124 | typedef std::priority_queue, ComparePartition > PartitionsPriorityQueue; 125 | 126 | // create answer-list with initial partition 127 | PartitionsPriorityQueue priorityQueue, answerList; 128 | priorityQueue.push(init); 129 | 130 | // assume values between 0 and 1 ! 131 | const Scalar lockingValue = 0.; 132 | 133 | while ( !priorityQueue.empty() && answerList.size() < kBest ) 134 | { 135 | // take first element from queue 136 | Partition currentPartition = priorityQueue.top(); 137 | priorityQueue.pop(); 138 | 139 | answerList.push(currentPartition); 140 | 141 | // for all triplets in this solution 142 | for (size_t e = 0; e < currentPartition.edges.size(); ++e) 143 | { 144 | auto & triplet = currentPartition.edges[e]; 145 | 146 | WeightMatrix P_ = currentPartition.w; // P' = P 147 | 148 | // exclude edge by setting weight in matrix to lockingValue -> NOT (x, y) 149 | P_(triplet.x, triplet.y) = lockingValue; 150 | 151 | // determine solution for changed matrix and create partition 152 | Edges S_ = Auction::solve(P_); 153 | 154 | #ifdef __ASSOCIATON_FINDER_DEBUG 155 | for (const auto & t : currentPartition.edges) 156 | { 157 | if ( t.x == triplet.x && t.y == triplet.y ) 158 | std::cout << "NOT "; 159 | std::cout << "(" << t.x << ", " << t.y << ") "; 160 | } 161 | std::cout << "sum = " << objectiveFunctionValue(S_) << std::endl; 162 | #endif 163 | 164 | if (S_.size() == P_.rows())// solution found? (rows >= cols!) 165 | { 166 | // sort edges by row 167 | std::sort(S_.begin(), S_.end(), [](const Edge & e1, const Edge & e2) {return e1.x < e2.x;}); 168 | 169 | priorityQueue.emplace(Partition(S_, P_, objectiveFunctionValue(S_))); 170 | 171 | } 172 | // remove all vertices that include row and column of current node 173 | // i.e. force using this edge 174 | for (size_t r = 0; r < rows; ++r ) 175 | currentPartition.w(r, triplet.y) = lockingValue; 176 | 177 | for (size_t c = 0; c < cols; ++c ) 178 | currentPartition.w(triplet.x, c) = lockingValue; 179 | 180 | // set edge back to original value 181 | currentPartition.w(triplet.x, triplet.y) = triplet.v = w(triplet.x, triplet.y); 182 | } 183 | } 184 | // create return list 185 | while( !answerList.empty() ) 186 | { 187 | resultingEdges.emplace_back(answerList.top().edges); 188 | answerList.pop(); 189 | } 190 | 191 | return resultingEdges; 192 | } 193 | }; 194 | 195 | 196 | #endif 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MurtyAlgorithm 2 | ============== 3 | 4 | Murty's Algorithm for m-best assignments; Implementation inspired by Miller's pseudocode 5 | 6 | Uses the auction algorithm to obtain the m-best solutions of the assignment problem 7 | 8 | 9 | Miller's pseudocode found at: 10 | * Miller, M.L.; Stone, H.S.; Cox, Ingemar J., "Optimizing Murty's ranked assignment method," 11 | * Aerospace and Electronic Systems, IEEE Transactions on , vol.33, no.3, pp.851,862, July 1997 12 | * doi: 10.1109/7.599256 13 | 14 | Original Murty's paper: 15 | * Katta G. Murty, An algorithm for ranking all the assignments in order of increasing cost, Operations 16 | * Research, Vol. 16, No. 3, pp. 682 – 687, May-June, 1968. 17 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Miller.h" 3 | using namespace std; 4 | 5 | int main() 6 | { 7 | // use cols > rows! 8 | 9 | 10 | MurtyMiller::WeightMatrix c_ij(20, 30); 11 | typedef MurtyMiller::Edges Edges; 12 | 13 | c_ij = MurtyMiller::WeightMatrix::Random(20, 30); 14 | 15 | for ( size_t r = 0; r < c_ij.rows(); ++r ) 16 | for ( size_t c = 0; c < c_ij.cols(); ++c ) 17 | if ( c_ij(r, c) < 0 ) c_ij(r, c) = -c_ij(r, c); 18 | 19 | c_ij /= c_ij.maxCoeff(); 20 | 21 | std::cerr << "c_ij = \n" << c_ij << std::endl; 22 | 23 | std::vector solutions = MurtyMiller::getMBestAssignments(c_ij, 100); 24 | 25 | for ( const auto & s : solutions ) 26 | { 27 | for ( const auto & e : s ) 28 | std::cerr << "(" << e.x << ", " << e.y << ") "; 29 | std::cerr << "sum = " << MurtyMiller::objectiveFunctionValue(s) << std::endl; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | --------------------------------------------------------------------------------