Hungarian Algorithm for Assignment Problem | Set 2 (Implementation)
Last Updated :
10 Apr, 2023
Given a 2D array, arr of size N*N where arr[i][j] denotes the cost to complete the jth job by the ith worker. Any worker can be assigned to perform any job. The task is to assign the jobs such that exactly one worker can perform exactly one job in such a way that the total cost of the assignment is minimized.
Example
Input: arr[][] = {{3, 5}, {10, 1}}
Output: 4
Explanation: The optimal assignment is to assign job 1 to the 1st worker, job 2 to the 2nd worker.
Hence, the optimal cost is 3 + 1 = 4.
Input: arr[][] = {{2500, 4000, 3500}, {4000, 6000, 3500}, {2000, 4000, 2500}}
Output: 4
Explanation: The optimal assignment is to assign job 2 to the 1st worker, job 3 to the 2nd worker and job 1 to the 3rd worker.
Hence, the optimal cost is 4000 + 3500 + 2000 = 9500.
Different approaches to solve this problem are discussed in this article.
Approach: The idea is to use the Hungarian Algorithm to solve this problem. The algorithm is as follows:
- For each row of the matrix, find the smallest element and subtract it from every element in its row.
- Repeat the step 1 for all columns.
- Cover all zeros in the matrix using the minimum number of horizontal and vertical lines.
- Test for Optimality: If the minimum number of covering lines is N, an optimal assignment is possible. Else if lines are lesser than N, an optimal assignment is not found and must proceed to step 5.
- Determine the smallest entry not covered by any line. Subtract this entry from each uncovered row, and then add it to each covered column. Return to step 3.
Consider an example to understand the approach:
Let the 2D array be:
2500 4000 3500
4000 6000 3500
2000 4000 2500
Step 1: Subtract minimum of every row. 2500, 3500 and 2000 are subtracted from rows 1, 2 and 3 respectively.
0 1500 1000
500 2500 0
0 2000 500
Step 2: Subtract minimum of every column. 0, 1500 and 0 are subtracted from columns 1, 2 and 3 respectively.
0 0 1000
500 1000 0
0 500 500
Step 3: Cover all zeroes with minimum number of horizontal and vertical lines.
Step 4: Since we need 3 lines to cover all zeroes, the optimal assignment is found.
2500 4000 3500
4000 6000 3500
2000 4000 2500
So the optimal cost is 4000 + 3500 + 2000 = 9500
For implementing the above algorithm, the idea is to use the max_cost_assignment() function defined in the dlib library. This function is an implementation of the Hungarian algorithm (also known as the Kuhn-Munkres algorithm) which runs in O(N3) time. It solves the optimal assignment problem.
Below is the implementation of the above approach:
C
#include <stdio.h>
#include <stdlib.h>
#include "hungarian.h"
void minCost( double **arr, int n) {
double **costMatrix = array_to_matrix(arr, n, n);
hungarian_t prob;
hungarian_init(&prob, costMatrix, n, n, HUNGARIAN_MODE_MINIMIZE_COST);
hungarian_solve(&prob);
double cost = prob.cost;
printf ( "Optimal cost: %.2f\n" , cost);
matrix_free(costMatrix, n);
}
int main() {
double arr[2][2] = {{3, 5}, {10, 1}};
minCost(( double **)arr, 2);
return 0;
}
|
C++
#include <iostream>
#include <fstream>
#include <vector>
#include "Hungarian.h"
using namespace std;
void minCost(vector<vector< double >> arr) {
int n = arr.size();
double **costMatrix = new double *[n];
for ( int i = 0; i < n; i++) {
costMatrix[i] = new double [n];
for ( int j = 0; j < n; j++) {
costMatrix[i][j] = arr[i][j];
}
}
HungarianAlgorithm algorithm;
vector< int > assignment;
double cost = algorithm.Solve(costMatrix, n, n, assignment);
cout << "Optimal cost: " << cost << endl;
for ( int i = 0; i < n; i++) {
delete [] costMatrix[i];
}
delete [] costMatrix;
}
int main() {
ifstream inputFile( "input.csv" );
vector<vector< double >> arr;
string line;
while (getline(inputFile, line)) {
vector< double > row;
size_t pos = 0;
string token;
while ((pos = line.find( "," )) != string::npos) {
token = line.substr(0, pos);
row.push_back(stod(token));
line.erase(0, pos + 1);
}
row.push_back(stod(line));
arr.push_back(row);
}
minCost(arr);
return 0;
}
|
Python
import dlib
def minCost(arr):
assignment = dlib.max_cost_assignment(arr)
print (dlib.assignment_cost(arr, assignment))
arr = dlib.matrix([[ 3 , 5 ], [ 10 , 1 ]])
minCost(arr)
|
Java
import com.jmatio.io.MatFileReader;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLDouble;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;
import org.apache.commons.math3.linear.SingularValueDecomposition;
import java.io.IOException;
public class AssignmentProblem {
public static void minCost( double [][] arr) {
RealMatrix matrix = new Array2DRowRealMatrix(arr);
HungarianAlgorithm algorithm = new HungarianAlgorithm(matrix);
int [] assignment = algorithm.execute();
double cost = 0.0 ;
for ( int i = 0 ; i < assignment.length; i++) {
int j = assignment[i];
cost += arr[i][j];
}
System.out.println( "Optimal cost: " + cost);
}
public static void main(String[] args) throws IOException {
MatFileReader reader = new MatFileReader( "input.mat" );
MLArray array = reader.getMLArray( "arr" );
double [][] arr = ((MLDouble) array).getArray();
minCost(arr);
}
}
|
C#
using System;
using System.IO;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using HungarianAlgorithmDotNet;
public class AssignmentProblem
{
public static void MinCost( double [,] arr)
{
var matrix = DenseMatrix.OfArray(arr);
var algorithm = new HungarianAlgorithm(matrix);
int [] assignment = algorithm.Run();
double cost = 0.0;
for ( int i = 0; i < assignment.Length; i++)
{
int j = assignment[i];
cost += arr[i, j];
}
Console.WriteLine( "Optimal cost: " + cost);
}
public static void Main()
{
var reader = new StreamReader( "input.csv" );
int rows = File.ReadLines( "input.csv" ).Count();
int cols = reader.ReadLine().Split( ',' ).Length;
double [,] arr = new double [rows, cols];
for ( int i = 0; i < rows; i++)
{
var line = reader.ReadLine().Split( ',' );
for ( int j = 0; j < cols; j++)
{
arr[i, j] = double .Parse(line[j]);
}
}
MinCost(arr);
}
}
|
Javascript
const hungarian = require( 'hungarian-algorithm-js' );
function minCost(arr) {
const result = hungarian(arr, true );
let cost = 0;
for (let i = 0; i < result.length; i++) {
const [row, col] = result[i];
cost += arr[row][col];
}
console.log(`Optimal cost: ${cost}`);
}
function main() {
const arr = [[3, 5], [10, 1]];
minCost(arr);
}
main();
|
PHP
require_once 'hungarian.php' ;
function minCost( $arr ) {
$h = new Hungarian( $arr );
$result = $h ->solve();
$cost = 0;
foreach ( $result as $row => $col ) {
$cost += $arr [ $row ][ $col ];
}
echo "Optimal cost: $cost\n" ;
}
function main() {
$arr = array ( array (3, 5), array (10, 1));
minCost( $arr );
}
main();
|
Time Complexity: O(N3)
Auxiliary Space: O(N2)
Share your thoughts in the comments
Please Login to comment...