public void AssignmentValue1() { int[] matches = new int[6]; AE.Fill(matches, i => i); Assert.AreEqual(6, GC.AssignmentValue(threecomp, matches)); }
public void LinearAssignmentUnique() { SparseMatrix profit = new SparseMatrix(4, 4); for (int i = 0; i < 10; i++) { profit[i, i] = (i + 1) / 2.0; } int[] best = GC.LinearAssignment(profit); int[] expected = new int[10]; AE.Fill(expected, i => i); Assert.IsTrue(expected.SequenceEqual(best)); }
/// <summary> /// Solve the linear assignment problem with the hungarian algorithm proposed by Kuhn. /// </summary> /// <param name="matrix">Utility matrix, where each row represent a worker and each column, a work. /// It is represented sparsely, but every index should have at least one entry (the return is a non-sparse list). /// This is for efficiency reasons, as the problem at hand doesn't require sparsity in the output.</param> /// <returns>Dense assignment list.</returns> private static int[] Hungarian(SparseMatrix matrix) { // initial feasible labeling var rowmax = matrix.FoldRows(Math.Max, 0); double[] labelx = new double[matrix.Width]; double[] labely = new double[matrix.Height]; int [] matchx = new int [labelx.Length]; int [] matchy = new int [labelx.Length]; bool [] visitx = new bool [labelx.Length]; // set represented by its indicator function bool [] visity = new bool [labely.Length]; // idem double[] slack = new double[labely.Length]; int [] parent = new int [labely.Length]; int root = -1; int iminslack = int.MaxValue; double delta = double.PositiveInfinity; foreach (var item in rowmax) { labelx[item.Key] = item.Value; } AE.Fill(matchx, -1); AE.Fill(matchy, -1); // find an unmatched worker to start building a tree while ((root = Array.IndexOf(matchx, -1)) != -1) { // annotate all its slacks AE.Fill(parent, root); AE.Fill(slack, i => labelx[root] + labely[i] - matrix[root, i]); Array.Clear(visitx, 0, visitx.Length); Array.Clear(visity, 0, visity.Length); visitx[root] = true; bool found = false; while (!found) { // find the minimal slack where "y is in T" iminslack = int.MaxValue; delta = double.PositiveInfinity; for (int i = 0; i < slack.Length; i++) { if (visity[i] == false && slack[i] < delta) { iminslack = i; delta = slack[i]; } } if (double.IsPositiveInfinity(delta)) { return(null); // there is no solution } // change the labels to tighten the slacks // { for (int i = 0; i < labelx.Length; i++) { if (visitx[i] == true) { labelx[i] -= delta; } } for (int i = 0; i < labely.Length; i++) { if (visity[i] == true) { labely[i] += delta; } else { slack[i] -= delta; } } // } visity[iminslack] = true; // for matched minimal slack, expand tree if (matchy[iminslack] != -1) { int match = matchy[iminslack]; visitx[match] = true; for (int i = 0; i < labely.Length; i++) { if (!visity[i]) { double mdelta = labelx[match] + labely[i] - matrix[match, i]; if (mdelta < slack[i]) { slack [i] = mdelta; parent[i] = match; } } } } // for unmatched minimal slack, the tree has an augmenting branch, // follow it and invert every edge else { found = true; } } // invert augmenting branch // { int px, py, ty; for (py = iminslack, px = parent[py]; px != root; py = ty, px = parent[py]) { ty = matchx[px]; matchx[px] = py; matchy[py] = px; } matchx[px] = py; matchy[py] = px; // } } return(matchx); }