http://www.hungarianalgorithm.com/examplehungarianalgorithm.php
我已经实现了所有步骤,直到最后一步,其中覆盖所有零(nxn矩阵的n)所需的最小行数。
最初,我计划进行一个贪婪的选择,我将逐列遍历2d数组并选择找到的第一个零。很快,发现这是一个糟糕的主意,甚至对我的小型5x5矩阵也不起作用(真正的东西很可能是n约为100-200的矩阵)。
每个列和行只能选择一个零。例如,假设选择了一个成本为[0] [1]的零,则无法在第0行或第1列中选择其他零(因为该算法将1个“工人”匹配到1个“工作”)。
对于矩阵成本(成本已被处理成最终形式):
{1 0 3 2 2
0 1 3 1 3
5 4 4 0 0
1 0 0 1 1
0 1 0 0 2}
使用“贪婪选择”,输出将为
int[] matched = new int[costs.length]{1, 0, 3, 2, 2};//Array stores column index of the zeros.
很明显,选择成本为零的成本[4] [3]是理想的选择,而不是贪婪地选择成本[2] [3],因为您随后可以选择成本[2] [4]。有人知道我可以实施一个相对简单的选择算法来解决这个问题吗?
如果您有兴趣,这是到目前为止的课程(我省略了不太相关的代码):
public class HungarianAlgorithm {
private int[][] costMatrix;//Untouched, original matrix, will not be changed during runtime.
private final int zeroCoveredOnce = 100;//The value used during the draw lines step to indicate that a position in int[][] cost has been covered once with a line. To indicate it has been covered twice, use 2x this value.
public HungarianAlgorithm(int[][] costMatrix)
{
this.costMatrix = costMatrix;
}
public int[] execute()
{
int[][] costs = costMatrix;//costs will be changed through execution.
Covering covering;
int lineCount;
costs = initialReduction(costs);
covering = coverZeros(costs);
costs = covering.returnCosts();
lineCount = covering.returnLineCount();
while(lineCount < costs.length)
{
costs = furtherReduction(costs);
covering = coverZeros(costs);
costs = covering.returnCosts();
lineCount = covering.returnLineCount();
}
return null;
}
private int[] assignment(int[][] costs)//The problematic one.
{
int[] assignment;
return null;
}
private int[][] initialReduction(int[][] costs)//Reduces rows, then columns. Perhaps should separate them but who knows we'll see.
{
int[] minOfRows = new int[costs[0].length];//int array of the lowest cost in each row of array
int[] minOfColumns = new int[costs.length];
minOfRows = returnRowMins(costs);
int tempRowCount = 0;
for(int[] row: costs)
{
for(int i: row)
{
i = i - minOfRows[tempRowCount];
}
tempRowCount++;
}
minOfColumns = returnColumnMins(costs);
for(int j = 0; j < costs.length; j++)
{
if(checkZerosInColumn(costs, j) == false)
{
for(int k = 0; k < costs.length; k++)
{
costs[k][j] = costs[k][j] - minOfColumns[j];
}
}
}
return costs;
}
private int[][] furtherReduction(int[][] costs)
{
int smallest = findSmallestUncoveredNumber(costs);
for(int[] row: costs)
{
for(int value: row)
{
if(value == zeroCoveredOnce*2)
{
value = value + smallest;
}
if(value != zeroCoveredOnce && value != zeroCoveredOnce*2)
{
value = value - smallest;
}
}
}
return costs;
}
private int findSmallestUncoveredNumber(int[][] costs)
{
int smallest = 0;
for(int i = 0; i < costs.length; i++)
{
smallest = smallest + costs[0][i];
}
for(int[] row: costs)
{
for(int value: row)
{
if(value < smallest)
{
smallest = value;
}
}
}
return smallest;
}
private Covering coverZeros(int[][] costs)
{
int lineCount = 0;
Covering covering;
for(int i = 0; i < costs.length; i++)
{
for(int j = 0; j < costs.length; j++)
{
if(costs[i][j] == 0)
{
costs = drawLines(costs, i, j);
++lineCount;
}
}
}
covering = new Covering(costs, lineCount);
return covering;
}
private int[][] drawLines(int[][] costs, int x, int y)
{
int orientation = determineOrientation(costs, x, y);
for(int i = 0; i < costs.length; i++)
{
if(orientation > 0)
{
if(costs[x][i] == zeroCoveredOnce)
{
costs[x][i] = zeroCoveredOnce*2;
}
if(costs[x][i] != zeroCoveredOnce && costs[x][i] != zeroCoveredOnce*2)
{
costs[x][i] = zeroCoveredOnce;
}
}
else
{
if(costs[i][y] == zeroCoveredOnce)
{
costs[i][y] = zeroCoveredOnce*2;
}
if(costs[i][y] != zeroCoveredOnce && costs[i][y] != zeroCoveredOnce*2)
{
costs[i][y] = zeroCoveredOnce;
}
}
}
return costs;
}
private int determineOrientation(int[][] costs, int x, int y)
{
int indicator = 0;//Increment for row zeros, decrement for column zeros.
for(int i = 0; i < costs.length; i++)
{
if(costs[x][i] == 0)
{
++indicator;
}
if(costs[i][y] == 0)
{
--indicator;
}
}
return indicator;
}
private boolean checkZerosInColumn(int[][] costs, int columnNumber)//Iterates through costs[n][columnNumber] and returns true and terminates the current for loop whenever there are zeros.
{
boolean hasZeros = false;
for(int i = columnNumber; i < costs.length; i++)
{
for(int j = 0; j < costs[columnNumber].length; j++)
{
if(costs[j][i] == 0)
{
hasZeros = true;
break;
}
}
}
return hasZeros;
}
private int[] returnRowMins(int[][] costs);
private int[] returnColumnMins(int[][] costs);
public int[][] returnCostMatrix();
public void setCostMatrix(int[][] costMatrix);
}