OSDN Git Service

cflib は plugins プロジェクトから,Stigmata直下のプロジェクトに移行したため,このリポジトリからは削除した.
[stigmata/stigmata-plugins.git] / osb / src / main / java / jp / sourceforge / stigmata / birthmarks / osb / hungarian / HungarianMethod.java
diff --git a/osb/src/main/java/jp/sourceforge/stigmata/birthmarks/osb/hungarian/HungarianMethod.java b/osb/src/main/java/jp/sourceforge/stigmata/birthmarks/osb/hungarian/HungarianMethod.java
new file mode 100644 (file)
index 0000000..0ca16a5
--- /dev/null
@@ -0,0 +1,305 @@
+package jp.sourceforge.stigmata.birthmarks.osb.hungarian;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+\r
+/**\r
+ * <p>\r
+ * ハンガリアン法により,最小和の組み合わせを選択する計算機.\r
+ * ハンガリアン法は以下の手順により行われる.\r
+ * </p>\r
+ * <ul>\r
+ *   <li>与えられた2次元配列の各列から,その列の最小値を引く.</li>\r
+ *   <li>与えられた2次元配列の各行から,その行の最小値を引く.</li>\r
+ *   <li>各行各列から,1つずつ0を選択できれば,その部分が最適な組み合わせであるため,そこで終了する.</li>\r
+ *   <li>そうでない場合は,0の生成作業へと移る\r
+ *    <ul>\r
+ *      <li>1度に多くの0が消えるよう線を入れる.</li>\r
+ *      <li>線で消されなかったセルの最小値<em>min</em>を求める.</li>\r
+ *      <li>線で消されなかった全てのセルから,求めた最小値<em>min</em>を減算する.</li>\r
+ *    </ul>\r
+ *   </li>\r
+ *   <li>最適な組み合わせを選択する.選択できれば終了.選択できなければ,もう一度0の生成作業を行う.</li>\r
+ * </ul>\r
+ */\r
+public class HungarianMethod{\r
+    private CostMatrix matrix;\r
+    private List<Cell> solutions = new ArrayList<Cell>();\r
+\r
+    public HungarianMethod(CostMatrix matrix){\r
+        this.matrix = matrix;\r
+    }\r
+\r
+    public synchronized Cell[] getSolutions(){\r
+        Collections.sort(solutions, new Comparator<Cell>(){\r
+            @Override\r
+            public int compare(Cell arg0, Cell arg1){\r
+                if(arg0.getX() < arg1.getX()){\r
+                    return -1;\r
+                }\r
+                else{\r
+                    return 1;\r
+                }\r
+            }\r
+        });\r
+        return solutions.toArray(new Cell[solutions.size()]);\r
+    }\r
+\r
+    public synchronized Line[] getLines(){\r
+        boolean[][] linedMatrix = new boolean[matrix.getSize()][matrix.getSize()];\r
+        List<Line> lines = getLines(matrix, linedMatrix);\r
+    \r
+        return lines.toArray(new Line[lines.size()]);\r
+    }\r
+\r
+    /**\r
+     * ハンガリアン法を解く.\r
+     * @return 最大和もしくは最小和.\r
+     * @see CostMatrix#isMax\r
+     */\r
+    public double solve(){\r
+        // step.1  \r
+        matrix.minimize();\r
+\r
+        boolean[][] linedMatrix = new boolean[matrix.getSize()][matrix.getSize()];\r
+        List<Line> lines = getLines(matrix, linedMatrix);\r
+        while(lines.size() < matrix.getSize()){\r
+            moreMinimize(matrix, lines, linedMatrix);\r
+            resetBooleanMatrix(linedMatrix);\r
+            lines = getLines(matrix, linedMatrix);\r
+        }\r
+        double solution = parseSolutions(matrix, lines);\r
+\r
+        return solution;\r
+    }\r
+\r
+    private void resetBooleanMatrix(boolean[][] matrix){\r
+        for(int i = 0; i < matrix.length; i++){\r
+            for(int j = 0; j < matrix[i].length; j++){\r
+                matrix[i][j] = false;\r
+            }\r
+        }\r
+    }\r
+\r
+    private double parseSolutions(CostMatrix matrix, List<Line> lines){\r
+        double solution = 0d;\r
+\r
+        boolean[][] solutionMatrix = new boolean[matrix.getSize()][matrix.getSize()];\r
+        solutions.clear();\r
+        while(solutions.size() < matrix.getSize()){\r
+            Cell cell = getSolution(matrix, solutionMatrix);\r
+            for(int i = 0; i < matrix.getSize(); i++){\r
+                solutionMatrix[cell.getX()][i] = true;\r
+                solutionMatrix[i][cell.getY()] = true;\r
+            }\r
+            if(cell.isAvailable()){\r
+                solution = solution + cell.getValue();\r
+            }\r
+            solutions.add(cell);\r
+        }\r
+        for(int i = solutions.size() - 1; i >= 0; i--){\r
+            if(!solutions.get(i).isAvailable()){\r
+                solutions.remove(i);\r
+            }\r
+        }\r
+        return solution;\r
+    }\r
+\r
+    private Cell getSolution(CostMatrix matrix, boolean[][] solutionMatrix){\r
+        for(int i = 0; i < matrix.getSize(); i++){\r
+            int count = 0;\r
+            int lastIndex = -1;\r
+            for(int j = 0; j < matrix.getSize(); j++){\r
+                if(matrix.isZero(i, j) && !solutionMatrix[i][j]){\r
+                    count++;\r
+                    lastIndex = j;\r
+                }\r
+            }\r
+            if(count == 1){\r
+                Cell cell;\r
+                if(matrix.isValidOriginal(i, lastIndex)){\r
+                    cell = new Cell(matrix.getOriginal(i, lastIndex), i, lastIndex);\r
+                }\r
+                else{\r
+                    cell = new Cell.EmptyCell(i, lastIndex);\r
+                }\r
+                return cell;\r
+            }\r
+        }\r
+        for(int j = 0; j < matrix.getSize(); j++){\r
+            int count = 0;\r
+            int lastIndex = -1;\r
+            for(int i = 0; i < matrix.getSize(); i++){\r
+                if(matrix.isZero(i, j) && !solutionMatrix[i][j]){\r
+                    count++;\r
+                    lastIndex = i;\r
+                }\r
+            }\r
+            if(count == 1){\r
+                Cell cell;\r
+                if(matrix.isValidOriginal(lastIndex, j)){\r
+                    cell = new Cell(matrix.getOriginal(lastIndex, j), lastIndex, j);\r
+                }\r
+                else{\r
+                    cell = new Cell.EmptyCell(lastIndex, j);\r
+                }\r
+                return cell;\r
+            }\r
+        }\r
+        for(int i = 0; i < matrix.getSize(); i++){\r
+            for(int j = 0; j < matrix.getSize(); j++){\r
+                if(matrix.isZero(i, j) && !solutionMatrix[i][j]){\r
+                    Cell cell;\r
+                    if(matrix.isValidOriginal(i, j)){\r
+                        cell = new Cell(matrix.getOriginal(i, j), i, j);\r
+                    }\r
+                    else{\r
+                        cell = new Cell.EmptyCell(i, j);\r
+                    }\r
+                    return cell;\r
+                }\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    private List<Line> getLines(CostMatrix matrix, boolean[][] linedMatrix){\r
+        List<Line> lines = new ArrayList<Line>();\r
+        List<Line> candidate;\r
+\r
+        while((candidate = getCandidateLines(matrix, linedMatrix)).size() > 0){\r
+            Line line = candidate.get(candidate.size() - 1);\r
+            lineOut(matrix, linedMatrix, line);\r
+            lines.add(line);\r
+        }\r
+        return lines;\r
+    }\r
+\r
+    /**\r
+     * 対象の2次元配列から,全ての線を取り出す.\r
+     * ただし,線には必ず0を含み,その0は既に引かれた線で選択されていないものとする.\r
+     * また,選択した線は,線に含まれる0の数,交差する線の数によりソートされる.\r
+     */\r
+    private List<Line> getCandidateLines(CostMatrix matrix, boolean[][] linedMatrix){\r
+        List<Line> candidateLines = new ArrayList<Line>();\r
+        for(int i = 0; i < matrix.getSize(); i++){\r
+            int count = 0;\r
+            int crossCount = 0;\r
+            for(int j = 0; j < matrix.getSize(); j++){\r
+                if(matrix.isZero(i, j) && !linedMatrix[i][j]){\r
+                    count++;\r
+                }\r
+                if(linedMatrix[i][j]){\r
+                    crossCount++;\r
+                }\r
+            }\r
+            if(count > 0){\r
+                candidateLines.add(new Line(i, count, crossCount, Line.Direction.ROW));\r
+            }\r
+        }\r
+        for(int j = 0; j < matrix.getSize(); j++){\r
+            int count = 0;\r
+            int crossCount = 0;\r
+            for(int i = 0; i < matrix.getSize(); i++){\r
+                if(matrix.isZero(i, j) && !linedMatrix[i][j]){\r
+                    count++;\r
+                }\r
+                if(linedMatrix[i][j]){\r
+                    crossCount++;\r
+                }\r
+            }\r
+            if(count > 0){\r
+                candidateLines.add(new Line(j, count, crossCount, Line.Direction.COLUMN));\r
+            }\r
+        }\r
+        Collections.sort(candidateLines);\r
+\r
+        return candidateLines;\r
+    }\r
+\r
+    private void lineOut(CostMatrix matrix, boolean[][] linedMatrix, Line line){\r
+        if(line.getDirection() == Line.Direction.COLUMN){\r
+            for(int i = 0; i < linedMatrix.length; i++){\r
+                linedMatrix[i][line.getIndex()] = true;\r
+            }\r
+        }\r
+        else{\r
+            for(int i = 0; i < linedMatrix[0].length; i++){\r
+                linedMatrix[line.getIndex()][i] = true;\r
+            }\r
+        }\r
+    }\r
+\r
+    private List<Cell> getCrossedLineCell(CostMatrix matrix, List<Line> lines){\r
+        List<Cell> cross = new ArrayList<Cell>();\r
+        for(int i = 0; i < lines.size() - 1; i++){\r
+            Line line1 = lines.get(i);\r
+            for(int j = 1; j < lines.size(); j++){\r
+                Line line2 = lines.get(j);\r
+                if(line1.getDirection() != line2.getDirection()){\r
+                    int row = line1.getIndex();\r
+                    int column = line2.getIndex();\r
+                    if(line1.getDirection() == Line.Direction.COLUMN){\r
+                        row = line2.getIndex();\r
+                        column = line1.getIndex();\r
+                    }\r
+                    cross.add(matrix.getCell(row, column));\r
+                }\r
+            }\r
+        }\r
+        return cross;\r
+    }\r
+\r
+    public synchronized void reduceMatrix(Line[] lineArray){\r
+        List<Line> lines = new ArrayList<Line>();\r
+        boolean[][] linedMatrix = new boolean[matrix.getSize()][matrix.getSize()];\r
+        for(Line line: lineArray){\r
+            lines.add(line);\r
+            for(int i = 0; i < linedMatrix.length; i++){\r
+                if(line.getDirection() == Line.Direction.COLUMN){\r
+                    linedMatrix[i][line.getIndex()] = true;\r
+                }\r
+                else{\r
+                    linedMatrix[line.getIndex()][i] = true;\r
+                }\r
+            }\r
+        }\r
+        moreMinimize(matrix, lines, linedMatrix);\r
+    }\r
+\r
+    /**\r
+     * ハンガリアン法のステップ3, 4に相当する.\r
+     * 線の引かれていない箇所から,最小値を引く.\r
+     */\r
+    private void moreMinimize(CostMatrix matrix, List<Line> lines, boolean[][] linedMatrix){\r
+        List<Cell> cross = getCrossedLineCell(matrix, lines);\r
+\r
+        // 線が引かれていない部分から最小値を選択する.\r
+        double min = 1;\r
+        for(int i = 0; i < matrix.getSize(); i++){\r
+            for(int j = 0; j < matrix.getSize(); j++){\r
+                if(!linedMatrix[i][j] && min > matrix.getValue(i, j)){\r
+                    min = matrix.getValue(i, j);\r
+                }\r
+            }\r
+        }\r
+\r
+        // 線が引かれていない箇所から,求めた最小値を減算する.\r
+        for(int i = 0; i < matrix.getSize(); i++){\r
+            for(int j = 0; j < matrix.getSize(); j++){\r
+                if(!linedMatrix[i][j]){\r
+                    matrix.setValue(matrix.getValue(i, j) - min, i, j);\r
+                }\r
+            }\r
+        }\r
+        // 線が交わっている箇所は減算した値を足す.\r
+        for(Cell cell: cross){\r
+            matrix.setValue(\r
+                matrix.getValue(cell.getX(), cell.getY()) + min,\r
+                cell.getX(), cell.getY()\r
+            );\r
+        }\r
+    }\r
+}
\ No newline at end of file