You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
505 lines
14 KiB
505 lines
14 KiB
/*
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2000 Robert A. Pilgrim
|
|
Murray State University
|
|
Dept. of Computer Science & Information Systems
|
|
Murray,Kentucky
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
*/
|
|
|
|
#pragma warning disable SX1101, SA1108, SA1119, SA1124, SA1200, SA1208, SA1214, SA1314, SA1403, SA1503, SA1514, SA1515, SA1519, SX1309
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace NzbDrone.Core.MediaFiles.TrackImport.Identification
|
|
{
|
|
public class Munkres
|
|
{
|
|
private double[,] C;
|
|
private readonly double[,] C_orig;
|
|
private int[,] M;
|
|
private int[,] path;
|
|
private int[] RowCover;
|
|
private int[] ColCover;
|
|
private readonly int n;
|
|
private readonly int nrow_orig;
|
|
private readonly int ncol_orig;
|
|
private int path_count;
|
|
private int path_row_0;
|
|
private int path_col_0;
|
|
private int step;
|
|
|
|
public Munkres(double[,] costMatrix)
|
|
{
|
|
C = PadMatrix(costMatrix);
|
|
n = C.GetLength(0);
|
|
nrow_orig = costMatrix.GetLength(0);
|
|
ncol_orig = costMatrix.GetLength(1);
|
|
|
|
C_orig = C.Clone() as double[,];
|
|
RowCover = new int[n];
|
|
ColCover = new int[n];
|
|
M = new int[n, n];
|
|
path = new int[(2 * n) + 1, 2];
|
|
|
|
step = 1;
|
|
}
|
|
|
|
public int[,] Marked
|
|
{
|
|
get
|
|
{
|
|
return M;
|
|
}
|
|
}
|
|
|
|
public List<Tuple<int, int>> Solution
|
|
{
|
|
get
|
|
{
|
|
var value = new List<Tuple<int, int>>();
|
|
for (var row = 0; row < nrow_orig; row++)
|
|
{
|
|
for (var col = 0; col < ncol_orig; col++)
|
|
{
|
|
if (M[row, col] == 1)
|
|
{
|
|
value.Add(Tuple.Create(row, col));
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
}
|
|
|
|
public double Cost
|
|
{
|
|
get
|
|
{
|
|
var soln = Solution;
|
|
return soln.Select(x => C_orig[x.Item1, x.Item2]).Sum();
|
|
}
|
|
}
|
|
|
|
private double[,] PadMatrix(double[,] matrix)
|
|
{
|
|
var newdim = Math.Max(matrix.GetLength(0), matrix.GetLength(1));
|
|
var outp = new double[newdim, newdim];
|
|
for (var row = 0; row < matrix.GetLength(0); row++)
|
|
{
|
|
for (var col = 0; col < matrix.GetLength(1); col++)
|
|
{
|
|
outp[row, col] = matrix[row, col];
|
|
}
|
|
}
|
|
|
|
return outp;
|
|
}
|
|
|
|
// For each row of the cost matrix, find the smallest element and subtract
|
|
// it from every element in its row. When finished, Go to Step 2.
|
|
private void step_one(ref int step)
|
|
{
|
|
double min_in_row;
|
|
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
min_in_row = C[r, 0];
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (C[r, c] < min_in_row)
|
|
{
|
|
min_in_row = C[r, c];
|
|
}
|
|
}
|
|
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
C[r, c] -= min_in_row;
|
|
}
|
|
}
|
|
|
|
step = 2;
|
|
}
|
|
|
|
// Find a zero (Z) in the resulting matrix. If there is no starred
|
|
// zero in its row or column, star Z. Repeat for each element in the
|
|
// matrix. Go to Step 3.
|
|
private void step_two(ref int step)
|
|
{
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (C[r, c] == 0 && RowCover[r] == 0 && ColCover[c] == 0)
|
|
{
|
|
M[r, c] = 1;
|
|
RowCover[r] = 1;
|
|
ColCover[c] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
RowCover[r] = 0;
|
|
}
|
|
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
ColCover[c] = 0;
|
|
}
|
|
|
|
step = 3;
|
|
}
|
|
|
|
// Cover each column containing a starred zero. If K columns are covered,
|
|
// the starred zeros describe a complete set of unique assignments. In this
|
|
// case, Go to DONE, otherwise, Go to Step 4.
|
|
private void step_three(ref int step)
|
|
{
|
|
int colcount;
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (M[r, c] == 1)
|
|
{
|
|
ColCover[c] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
colcount = 0;
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (ColCover[c] == 1)
|
|
{
|
|
colcount += 1;
|
|
}
|
|
}
|
|
|
|
if (colcount >= n)
|
|
{
|
|
step = 7;
|
|
}
|
|
else
|
|
{
|
|
step = 4;
|
|
}
|
|
}
|
|
|
|
// methods to support step 4
|
|
private void find_a_zero(ref int row, ref int col)
|
|
{
|
|
var r = 0;
|
|
int c;
|
|
bool done;
|
|
row = -1;
|
|
col = -1;
|
|
done = false;
|
|
while (!done)
|
|
{
|
|
c = 0;
|
|
while (true)
|
|
{
|
|
if (C[r, c] == 0 && RowCover[r] == 0 && ColCover[c] == 0)
|
|
{
|
|
row = r;
|
|
col = c;
|
|
done = true;
|
|
}
|
|
|
|
c += 1;
|
|
if (c >= n || done)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
r += 1;
|
|
if (r >= n)
|
|
{
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool star_in_row(int row)
|
|
{
|
|
var tmp = false;
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (M[row, c] == 1)
|
|
{
|
|
tmp = true;
|
|
}
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
private void find_star_in_row(int row, ref int col)
|
|
{
|
|
col = -1;
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (M[row, c] == 1)
|
|
{
|
|
col = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find a noncovered zero and prime it. If there is no starred zero
|
|
// in the row containing this primed zero, Go to Step 5. Otherwise,
|
|
// cover this row and uncover the column containing the starred zero.
|
|
// Continue in this manner until there are no uncovered zeros left.
|
|
// Save the smallest uncovered value and Go to Step 6.
|
|
private void step_four(ref int step)
|
|
{
|
|
var row = -1;
|
|
var col = -1;
|
|
bool done;
|
|
|
|
done = false;
|
|
while (!done)
|
|
{
|
|
find_a_zero(ref row, ref col);
|
|
if (row == -1)
|
|
{
|
|
done = true;
|
|
step = 6;
|
|
}
|
|
else
|
|
{
|
|
M[row, col] = 2;
|
|
if (star_in_row(row))
|
|
{
|
|
find_star_in_row(row, ref col);
|
|
RowCover[row] = 1;
|
|
ColCover[col] = 0;
|
|
}
|
|
else
|
|
{
|
|
done = true;
|
|
step = 5;
|
|
path_row_0 = row;
|
|
path_col_0 = col;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// methods to support step 5
|
|
private void find_star_in_col(int c, ref int r)
|
|
{
|
|
r = -1;
|
|
for (var i = 0; i < n; i++)
|
|
{
|
|
if (M[i, c] == 1)
|
|
{
|
|
r = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void find_prime_in_row(int r, ref int c)
|
|
{
|
|
for (var j = 0; j < n; j++)
|
|
{
|
|
if (M[r, j] == 2)
|
|
{
|
|
c = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void augment_path()
|
|
{
|
|
for (var p = 0; p < path_count; p++)
|
|
{
|
|
if (M[path[p, 0], path[p, 1]] == 1)
|
|
{
|
|
M[path[p, 0], path[p, 1]] = 0;
|
|
}
|
|
else
|
|
{
|
|
M[path[p, 0], path[p, 1]] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void clear_covers()
|
|
{
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
RowCover[r] = 0;
|
|
}
|
|
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
ColCover[c] = 0;
|
|
}
|
|
}
|
|
|
|
private void erase_primes()
|
|
{
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (M[r, c] == 2)
|
|
{
|
|
M[r, c] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Construct a series of alternating primed and starred zeros as follows.
|
|
// Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
|
|
// the starred zero in the column of Z0 (if any). Let Z2 denote the primed zero
|
|
// in the row of Z1 (there will always be one). Continue until the series
|
|
// terminates at a primed zero that has no starred zero in its column.
|
|
// Unstar each starred zero of the series, star each primed zero of the series,
|
|
// erase all primes and uncover every line in the matrix. Return to Step 3.
|
|
private void step_five(ref int step)
|
|
{
|
|
bool done;
|
|
var r = -1;
|
|
var c = -1;
|
|
|
|
path_count = 1;
|
|
path[path_count - 1, 0] = path_row_0;
|
|
path[path_count - 1, 1] = path_col_0;
|
|
done = false;
|
|
while (!done)
|
|
{
|
|
find_star_in_col(path[path_count - 1, 1], ref r);
|
|
if (r > -1)
|
|
{
|
|
path_count += 1;
|
|
path[path_count - 1, 0] = r;
|
|
path[path_count - 1, 1] = path[path_count - 2, 1];
|
|
}
|
|
else
|
|
{
|
|
done = true;
|
|
}
|
|
|
|
if (!done)
|
|
{
|
|
find_prime_in_row(path[path_count - 1, 0], ref c);
|
|
path_count += 1;
|
|
path[path_count - 1, 0] = path[path_count - 2, 0];
|
|
path[path_count - 1, 1] = c;
|
|
}
|
|
}
|
|
|
|
augment_path();
|
|
clear_covers();
|
|
erase_primes();
|
|
step = 3;
|
|
}
|
|
|
|
// methods to support step 6
|
|
private void find_smallest(ref double minval)
|
|
{
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (RowCover[r] == 0 && ColCover[c] == 0)
|
|
{
|
|
if (minval > C[r, c])
|
|
{
|
|
minval = C[r, c];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the value found in Step 4 to every element of each covered row, and subtract
|
|
// it from every element of each uncovered column. Return to Step 4 without
|
|
// altering any stars, primes, or covered lines.
|
|
private void step_six(ref int step)
|
|
{
|
|
var minval = double.MaxValue;
|
|
find_smallest(ref minval);
|
|
for (var r = 0; r < n; r++)
|
|
{
|
|
for (var c = 0; c < n; c++)
|
|
{
|
|
if (RowCover[r] == 1)
|
|
{
|
|
C[r, c] += minval;
|
|
}
|
|
|
|
if (ColCover[c] == 0)
|
|
{
|
|
C[r, c] -= minval;
|
|
}
|
|
}
|
|
}
|
|
|
|
step = 4;
|
|
}
|
|
|
|
public void Run()
|
|
{
|
|
var done = false;
|
|
while (!done)
|
|
{
|
|
switch (step)
|
|
{
|
|
case 1:
|
|
step_one(ref step);
|
|
break;
|
|
case 2:
|
|
step_two(ref step);
|
|
break;
|
|
case 3:
|
|
step_three(ref step);
|
|
break;
|
|
case 4:
|
|
step_four(ref step);
|
|
break;
|
|
case 5:
|
|
step_five(ref step);
|
|
break;
|
|
case 6:
|
|
step_six(ref step);
|
|
break;
|
|
case 7:
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|