web-dev-qa-db-ja.com

ハンガリーのアルゴリズム:ゼロをカバーするための最小行数を見つける?

ハンガリーのアルゴリズム を実装しようとしていますが、 ステップ5 で立ち往生しています。基本的に、n X n数の行列、行列のゼロがカバーされるように、垂直線と水平線の最小数を見つけるにはどうすればよいですか?

誰かがこの質問を this の重複としてマークする前に、そこに記載されている解決策は正しくなく、他の誰か そこに投稿されたコードのバグにも遭遇しました

私はコードを探しているのではなく、これらの線を引くことができる概念を探しています...

編集:単純な(しかし間違った)欲張りアルゴリズムを投稿しないでください:この入力を考えると:

(0, 1, 0, 1, 1)
(1, 1, 0, 1, 1)
(1, 0, 0, 0, 1)
(1, 1, 0, 1, 1)
(1, 0, 0, 1, 0)

私は選択します、列2は明らかに(0-インデックス付き):

(0, 1, x, 1, 1)
(1, 1, x, 1, 1)
(1, 0, x, 0, 1)
(1, 1, x, 1, 1)
(1, 0, x, 1, 0)

これで、2つの「残りの」ゼロがある行2または列1のいずれかを選択できます。 col2を選択すると、次のパスで誤った解決策になります。

(0, x, x, 1, 1)
(1, x, x, 1, 1)
(1, x, x, 0, 1)
(1, x, x, 1, 1)
(1, x, x, 1, 0)

正しい解決策は、4行を使用することです。

(x, x, x, x, x)
(1, 1, x, 1, 1)
(x, x, x, x, x)
(1, 1, x, 1, 1)
(x, x, x, x, x)
24
pathikrit

更新

私はあなたが投稿したリンクによって提供されるのと同じステップでハンガリーのアルゴリズムを実装しました: ハンガリーのアルゴリズム

コメント付きのファイルは次のとおりです: Github

ステップ3のアルゴリズム(貪欲の改善):(このコードは非常に詳細で、描画する線の選択の概念を理解するのに適しています:水平vs垂直。しかしこのステップコードは、 Github )の私のコードで改善されていることに注意してください

  • 入力行列の各xy位置について、垂直方向と水平方向のゼロの最大数を計算し、結果をm2という別の配列に格納します。
  • 計算中に、水平ゼロ>垂直ゼロの場合、計算された数値は負に変換されます。 (後で使用するために選択した方向を区別するためだけに)
  • m2配列内のすべての要素をループします。値が正の場合は、配列m3に垂直線を描画し、値が負の場合は、m3に水平線を描画します。

以下の例+コードに従って、アルゴリズムをさらに理解してください:

3つの配列を作成します:

  • m1:最初の配列、入力値を保持します
  • m2:2番目の配列、各x、y位置でmaxZeroes(vertical、horizo​​ntal)を保持
  • m3:3番目の配列、最後の行を保持します(0インデックスがカバーされていない、1インデックスがカバーされている)

2つの関数を作成します:

  • hvMax(m1、row、col);水平または垂直のゼロの最大数を返します。 (正の数は垂直を意味し、負の数は水平を意味します)
  • clearNeighbours(m2、m3、row、col); voidメソッドの場合、行colインデックスの値が負の場合は水平方向の近傍をクリアし、正の場合は垂直方向の近傍をクリアします。さらに、ゼロビットを1に反転することにより、m3配列の行を設定します。

コード

public class Hungarian {
    public static void main(String[] args) {
        // m1 input values
        int[][] m1 = { { 0, 1, 0, 1, 1 }, { 1, 1, 0, 1, 1 }, { 1, 0, 0, 0, 1 },
                { 1, 1, 0, 1, 1 }, { 1, 0, 0, 1, 0 } };

        // int[][] m1 = { {13,14,0,8},
        // {40,0,12,40},
        // {6,64,0,66},
        // {0,1,90,0}};

        // int[][] m1 = { {0,0,100},
        // {50,100,0},
        // {0,50,50}};

        // m2 max(horizontal,vertical) values, with negative number for
        // horizontal, positive for vertical
        int[][] m2 = new int[m1.length][m1.length];

        // m3 where the line are drawen
        int[][] m3 = new int[m1.length][m1.length];

        // loop on zeroes from the input array, and sotre the max num of zeroes
        // in the m2 array
        for (int row = 0; row < m1.length; row++) {
            for (int col = 0; col < m1.length; col++) {
                if (m1[row][col] == 0)
                    m2[row][col] = hvMax(m1, row, col);
            }
        }

        // print m1 array (Given input array)
        System.out.println("Given input array");
        for (int row = 0; row < m1.length; row++) {
            for (int col = 0; col < m1.length; col++) {
                System.out.print(m1[row][col] + "\t");
            }
            System.out.println();
        }

        // print m2 array 
        System.out
                .println("\nm2 array (max num of zeroes from horizontal vs vertical) (- for horizontal and + for vertical)");
        for (int row = 0; row < m1.length; row++) {
            for (int col = 0; col < m1.length; col++) {
                System.out.print(m2[row][col] + "\t");
            }
            System.out.println();
        }

        // Loop on m2 elements, clear neighbours and draw the lines
        for (int row = 0; row < m1.length; row++) {
            for (int col = 0; col < m1.length; col++) {
                if (Math.abs(m2[row][col]) > 0) {
                    clearNeighbours(m2, m3, row, col);
                }
            }
        }

        // prinit m3 array (Lines array)
        System.out.println("\nLines array");
        for (int row = 0; row < m1.length; row++) {
            for (int col = 0; col < m1.length; col++) {
                System.out.print(m3[row][col] + "\t");
            }
            System.out.println();
        }
    }

    // max of vertical vs horizontal at index row col
    public static int hvMax(int[][] m1, int row, int col) {
        int vertical = 0;
        int horizontal = 0;

        // check horizontal
        for (int i = 0; i < m1.length; i++) {
            if (m1[row][i] == 0)
                horizontal++;
        }

        // check vertical
        for (int i = 0; i < m1.length; i++) {
            if (m1[i][col] == 0)
                vertical++;
        }

        // negative for horizontal, positive for vertical
        return vertical > horizontal ? vertical : horizontal * -1;
    }

    // clear the neighbors of the picked largest value, the sign will let the
    // app decide which direction to clear
    public static void clearNeighbours(int[][] m2, int[][] m3, int row, int col) {
        // if vertical
        if (m2[row][col] > 0) {
            for (int i = 0; i < m2.length; i++) {
                if (m2[i][col] > 0)
                    m2[i][col] = 0; // clear neigbor
                m3[i][col] = 1; // draw line
            }
        } else {
            for (int i = 0; i < m2.length; i++) {
                if (m2[row][i] < 0)
                    m2[row][i] = 0; // clear neigbor
                m3[row][i] = 1; // draw line
            }
        }

        m2[row][col] = 0;
        m3[row][col] = 1;
    }
}

出力

Given input array
0   1   0   1   1   
1   1   0   1   1   
1   0   0   0   1   
1   1   0   1   1   
1   0   0   1   0   

m2 array (max num of zeroes from horizontal vs vertical) (- for horizontal and + for vertical)
-2  0   5   0   0   
0   0   5   0   0   
0   -3  5   -3  0   
0   0   5   0   0   
0   -3  5   0   -3  

Lines array
1   1   1   1   1   
0   0   1   0   0   
1   1   1   1   1   
0   0   1   0   0   
1   1   1   1   1   

PS:最初のループがmax(horizo​​ntal、vertical)を取ることによって計算を行うので、あなたが指摘した例は決して起こりません。そしてそれらをm2に保存します。したがって、-3は水平線を引くことを意味し、-3は水平ゼロと垂直ゼロの間の最大値をとることによって計算されたため、col1は選択されません。したがって、要素での最初の反復で、プログラムは線の描画方法をチェックし、2回目の反復で、プログラムは線を描画します。

14
CMPS

Amirのコードが失敗する場合があります。

次のm1について考えてみます。

 0  0  1

 0  1  1

 1  0  1

最善の解決策は、最初の2列に垂直線を引くことです。

Amirのコードは次のm2を与えます:

-2 -2  0

 2  0  0

 0  2  0

その結果、最初の行に2本の垂直線が線として描画されます。

私には、問題はタイブレイクのケースであるように思われます。

return vertical > horizontal ? vertical : horizontal * -1;

コードの記述方法により、非常によく似たm1は失敗しません。

 0  1  1

 1  0  1

 0  0  1

クリア機能は、これらのセルに到達する前にm2から-2値をクリアするため、最初の行が一番下に移動します。最初のケースでは、-2の値が最初にヒットされるため、最初の行に水平線が引かれます。

私はこれを通して少し取り組んできました、そしてこれは私が持っているものです。同点の場合は、値を設定したり、それらのセルに線を引いたりしないでください。これは、前述のマトリックスの場合をカバーしています。これで完了です。

明らかに、カバーされていない0が残る状況があります。以下は、Amirの方法(m1)で失敗する行列の別の例です。

 0 0 1 1 1
 0 1 0 1 1
 0 1 1 0 1
 1 1 0 0 1
 1 1 1 1 1

最適なソリューションは4行です。最初の4列。

アミールの方法はm2を与える:

 3 -2  0  0  0
 3  0 -2  0  0
 3  0  0 -2  0
 0  0 -2 -2  0
 0  0  0  0  0

これは最初の4行と最初の列に線を引きます(5本の線を与える間違った解決策)。繰り返しになりますが、タイブレーカーケースが問題です。タイの値を設定せず、手順を繰り返すことでこれを解決します。

同点を無視すると、m2が得られます。

 3 -2  0  0  0
 3  0  0  0  0
 3  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

これにより、最初の行と最初の列のみがカバーされます。次に、カバーされている0を取り出して、新しいm1を作成します。

 1 1 1 1 1
 1 1 0 1 1
 1 1 1 0 1
 1 1 0 0 1
 1 1 1 1 1

次に、解決策に到達するまで、手順を繰り返します(同点は無視します)。新しいm2について繰り返します。

 0  0  0  0  0
 0  0  2  0  0
 0  0  0  2  0
 0  0  0  0  0
 0  0  0  0  0

これは、2番目と3番目の列を通る2本の垂直線につながります。これですべての0がカバーされ、必要なのは4行だけです(これは最初の4列を並べる代わりになります)。上記の行列は2回の反復のみを必要とし、タイのセット内にネストされたタイのセットがない限り、これらのケースのほとんどは2回の反復のみを必要とすると思います。思いついたのですが、管理が難しくなりました。

悲しいことに、これは十分ではありません。永遠に結びついたままになる場合があるからです。特に、「互いに素な結合セルのセット」がある場合。次の2つの例を描く以外に、これを他にどのように説明するかわからない。

 0 0 1 1
 0 1 1 1
 1 0 1 1
 1 1 1 0

または

 0 0 1 1 1
 0 1 1 1 1
 1 0 1 1 1
 1 1 1 0 0
 1 1 1 0 0

これら2つの例の左上の3x3サブ行列は、元の例と同じです。この例の右下に1行または2行/列を追加しました。新しく追加されたゼロは、新しい行と列が交差する場所だけです。わかりやすくするために説明します。

私が説明した反復法では、これらの行列は無限ループに陥ります。ゼロは常にタイのままになります(列数と行数)。この時点で、少なくとも私が想像できることから、同点の場合は方向を任意に選択することは理にかなっています。

私が遭遇している唯一の問題は、ループの停止基準を設定することです。 2回の反復(または任意のn)で十分であるとは想定できませんが、行列内に無限ループしか残っていないかどうかを検出する方法もわかりません。これらの互いに素な集合を計算で記述する方法はまだわかりません。

これまでに(MATLABスクリプトで)思いついたことを実行するためのコードは次のとおりです。

function [Lines, AllRows, AllCols] = FindMinLines(InMat)

%The following code finds the minimum set of lines (rows and columns)
%required to cover all of the true-valued cells in a matrix. If using for
%the Hungarian problem where 'true-values' are equal to zero, make the
%necessary changes. This code is not complete, since it will be caught in 
%an infinite loop in the case of disjoint-tied-sets

%If passing in a matrix where 0s are the cells of interest, uncomment the
%next line
%InMat = InMat == 0;

%Assume square matrix
Count = length(InMat);
Lines = zeros(Count);

%while there are any 'true' values not covered by lines

while any(any(~Lines & InMat))
    %Calculate row-wise and col-wise totals of 'trues' not-already-covered
    HorzCount = repmat(sum(~Lines & InMat, 2), 1, Count).*(~Lines & InMat);
    VertCount = repmat(sum(~Lines & InMat, 1), Count, 1).*(~Lines & InMat);

    %Calculate for each cell the difference between row-wise and col-wise
    %counts. I.e. row-oriented cells will have a negative number, col-oriented
    %cells will have a positive numbers, ties and 'non-trues' will be 0.
    %Non-zero values indicate lines to be drawn where orientation is determined
    %by sign. 
    DiffCounts = VertCount - HorzCount;

    %find the row and col indices of the lines
    HorzIdx = any(DiffCounts < 0, 2);
    VertIdx = any(DiffCounts > 0, 1);

    %Set the horizontal and vertical indices of the Lines matrix to true
    Lines(HorzIdx, :) = true;
    Lines(:, VertIdx) = true;
end

%compute index numbers to be returned. 
AllRows = [find(HorzIdx); find(DisjTiedRows)];
AllCols = find(VertIdx);

end
4
mhermher

欲張りアルゴリズムが機能しない場合があります。

まず、問題を次のように再定式化することができます。2部グラフが与えられた場合、最小頂点被覆を見つけます。この問題では、2n個のノードがあり、nは行、nは列です。対応する列と行の交点の要素がゼロの場合、2つのノード間にエッジがあります。頂点被覆は、各エッジがそのセットのノードに入射するようなノード(行と列)のセットです(各ゼロは行または列でカバーされます)。

これはよく知られている問題であり、最大一致を見つけることでO(n ^ 3)で解決できます。詳細については ウィキペディア を確認してください

4
Wanderer

ステップ5:マトリックス内の線の描画は、マトリックスの長さの最大評価で対角線上で評価されます。

http://www.wikihow.com/Use-the-Hungarian-Algorithm に基づき、ステップ1〜8のみ。

コードスニペットを実行し、コンソールで結果を確認します

コンソール出力

horizontal line (row): {"0":0,"2":2,"4":4}
vertical line (column): {"2":2}

Step 5: Matrix
0  1  0  1  1  
1  1  0  1  1  
1  0  0  0  1  
1  1  0  1  1  
1  0  0  1  0  

Smallest number in uncovered matrix: 1
Step 6: Matrix
x  x  x  x  x  
1  1  x  1  1  
x  x  x  x  x  
1  1  x  1  1  
x  x  x  x  x

JSFiddle: http://jsfiddle.net/jjcosare/6Lpz5gt9/2/

// http://www.wikihow.com/Use-the-Hungarian-Algorithm

var inputMatrix = [
  [0, 1, 0, 1, 1],
  [1, 1, 0, 1, 1],
  [1, 0, 0, 0, 1],
  [1, 1, 0, 1, 1],
  [1, 0, 0, 1, 0]
];

//var inputMatrix = [
//      [10, 19, 8, 15],
//      [10, 18, 7, 17],
//      [13, 16, 9, 14],
//      [12, 19, 8, 18],
//      [14, 17, 10, 19]
//    ];

var matrix = inputMatrix;
var HungarianAlgorithm = {};

HungarianAlgorithm.step1 = function(stepNumber) {

  console.log("Step " + stepNumber + ": Matrix");

  var currentNumber = 0;
  for (var i = 0; i < matrix.length; i++) {
    var sb = "";
    for (var j = 0; j < matrix[i].length; j++) {
      currentNumber = matrix[i][j];
      sb += currentNumber + "  ";
    }
    console.log(sb);
  }
}

HungarianAlgorithm.step2 = function() {
  var largestNumberInMatrix = getLargestNumberInMatrix(matrix);
  var rowLength = matrix.length;
  var columnLength = matrix[0].length;
  var dummyMatrixToAdd = 0;
  var isAddColumn = rowLength > columnLength;
  var isAddRow = columnLength > rowLength;

  if (isAddColumn) {
    dummyMatrixToAdd = rowLength - columnLength;
    for (var i = 0; i < rowLength; i++) {
      for (var j = columnLength; j < (columnLength + dummyMatrixToAdd); j++) {
        matrix[i][j] = largestNumberInMatrix;
      }
    }
  } else if (isAddRow) {
    dummyMatrixToAdd = columnLength - rowLength;
    for (var i = rowLength; i < (rowLength + dummyMatrixToAdd); i++) {
      matrix[i] = [];
      for (var j = 0; j < columnLength; j++) {
        matrix[i][j] = largestNumberInMatrix;
      }
    }
  }

  HungarianAlgorithm.step1(2);
  console.log("Largest number in matrix: " + largestNumberInMatrix);

  function getLargestNumberInMatrix(matrix) {
    var largestNumberInMatrix = 0;
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      for (var j = 0; j < matrix[i].length; j++) {
        currentNumber = matrix[i][j];
        largestNumberInMatrix = (largestNumberInMatrix > currentNumber) ?
          largestNumberInMatrix : currentNumber;
      }
    }
    return largestNumberInMatrix;
  }
}

HungarianAlgorithm.step3 = function() {
  var smallestNumberInRow = 0;
  var currentNumber = 0;

  for (var i = 0; i < matrix.length; i++) {
    smallestNumberInRow = getSmallestNumberInRow(matrix, i);
    console.log("Smallest number in row[" + i + "]: " + smallestNumberInRow);
    for (var j = 0; j < matrix[i].length; j++) {
      currentNumber = matrix[i][j];
      matrix[i][j] = currentNumber - smallestNumberInRow;
    }
  }

  HungarianAlgorithm.step1(3);

  function getSmallestNumberInRow(matrix, rowIndex) {
    var smallestNumberInRow = matrix[rowIndex][0];
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[rowIndex][i];
      smallestNumberInRow = (smallestNumberInRow < currentNumber) ?
        smallestNumberInRow : currentNumber;
    }
    return smallestNumberInRow;
  }
}

HungarianAlgorithm.step4 = function() {
  var smallestNumberInColumn = 0;
  var currentNumber = 0;

  for (var i = 0; i < matrix.length; i++) {
    smallestNumberInColumn = getSmallestNumberInColumn(matrix, i);
    console.log("Smallest number in column[" + i + "]: " + smallestNumberInColumn);
    for (var j = 0; j < matrix[i].length; j++) {
      currentNumber = matrix[j][i];
      matrix[j][i] = currentNumber - smallestNumberInColumn;
    }
  }

  HungarianAlgorithm.step1(4);

  function getSmallestNumberInColumn(matrix, columnIndex) {
    var smallestNumberInColumn = matrix[0][columnIndex];
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[i][columnIndex];
      smallestNumberInColumn = (smallestNumberInColumn < currentNumber) ?
        smallestNumberInColumn : currentNumber;
    }
    return smallestNumberInColumn;
  }
}

var rowLine = {};
var columnLine = {};
HungarianAlgorithm.step5 = function() {
  var zeroNumberCountRow = 0;
  var zeroNumberCountColumn = 0;

  rowLine = {};
  columnLine = {};
  for (var i = 0; i < matrix.length; i++) {
    zeroNumberCountRow = getZeroNumberCountInRow(matrix, i);
    zeroNumberCountColumn = getZeroNumberCountInColumn(matrix, i);
    if (zeroNumberCountRow > zeroNumberCountColumn) {
      rowLine[i] = i;
      if (zeroNumberCountColumn > 1) {
        columnLine[i] = i;
      }
    } else if (zeroNumberCountRow < zeroNumberCountColumn) {
      columnLine[i] = i;
      if (zeroNumberCountRow > 1) {
        rowLine[i] = i;
      }
    } else {
      if ((zeroNumberCountRow + zeroNumberCountColumn) > 2) {
        rowLine[i] = i;
        columnLine[i] = i;
      }
    }
  }

  var zeroCount = 0;
  for (var i in columnLine) {
    zeroCount = getZeroNumberCountInColumnLine(matrix, columnLine[i], rowLine);
    if (zeroCount == 0) {
      delete columnLine[i];
    }
  }
  for (var i in rowLine) {
    zeroCount = getZeroNumberCountInRowLine(matrix, rowLine[i], columnLine);
    if (zeroCount == 0) {
      delete rowLine[i];
    }
  }

  console.log("horizontal line (row): " + JSON.stringify(rowLine));
  console.log("vertical line (column): " + JSON.stringify(columnLine));

  HungarianAlgorithm.step1(5);

  //if ((Object.keys(rowLine).length + Object.keys(columnLine).length) == matrix.length) {
    // TODO:
    // HungarianAlgorithm.step9();
  //} else {
  //  HungarianAlgorithm.step6();
  //  HungarianAlgorithm.step7();
  //  HungarianAlgorithm.step8();
  //}

  function getZeroNumberCountInColumnLine(matrix, columnIndex, rowLine) {
    var zeroNumberCount = 0;
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[i][columnIndex];
      if (currentNumber == 0 && !(rowLine[i] == i)) {
        zeroNumberCount++
      }
    }
    return zeroNumberCount;
  }

  function getZeroNumberCountInRowLine(matrix, rowIndex, columnLine) {
    var zeroNumberCount = 0;
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[rowIndex][i];
      if (currentNumber == 0 && !(columnLine[i] == i)) {
        zeroNumberCount++
      }
    }
    return zeroNumberCount;
  }

  function getZeroNumberCountInColumn(matrix, columnIndex) {
    var zeroNumberCount = 0;
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[i][columnIndex];
      if (currentNumber == 0) {
        zeroNumberCount++
      }
    }
    return zeroNumberCount;
  }

  function getZeroNumberCountInRow(matrix, rowIndex) {
    var zeroNumberCount = 0;
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[rowIndex][i];
      if (currentNumber == 0) {
        zeroNumberCount++
      }
    }
    return zeroNumberCount;
  }
}

HungarianAlgorithm.step6 = function() {
  var smallestNumberInUncoveredMatrix = getSmallestNumberInUncoveredMatrix(matrix, rowLine, columnLine);
  console.log("Smallest number in uncovered matrix: " + smallestNumberInUncoveredMatrix);

  var columnIndex = 0;
  for (var i in columnLine) {
    columnIndex = columnLine[i];
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[i][columnIndex];
      //matrix[i][columnIndex] = currentNumber + smallestNumberInUncoveredMatrix;
      matrix[i][columnIndex] = "x";
    }
  }

  var rowIndex = 0;
  for (var i in rowLine) {
    rowIndex = rowLine[i];
    for (var i = 0; i < matrix.length; i++) {
      currentNumber = matrix[rowIndex][i];
      //matrix[rowIndex][i] = currentNumber + smallestNumberInUncoveredMatrix;
      matrix[rowIndex][i] = "x";
    }
  }

  HungarianAlgorithm.step1(6);

  function getSmallestNumberInUncoveredMatrix(matrix, rowLine, columnLine) {
    var smallestNumberInUncoveredMatrix = null;;
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      if (rowLine[i]) {
        continue;
      }
      for (var j = 0; j < matrix[i].length; j++) {
        if (columnLine[j]) {
          continue;
        }

        currentNumber = matrix[i][j];
        if (!smallestNumberInUncoveredMatrix) {
          smallestNumberInUncoveredMatrix = currentNumber;
        }

        smallestNumberInUncoveredMatrix =
          (smallestNumberInUncoveredMatrix < currentNumber) ?
          smallestNumberInUncoveredMatrix : currentNumber;
      }
    }
    return smallestNumberInUncoveredMatrix;
  }
}

HungarianAlgorithm.step7 = function() {
  var smallestNumberInMatrix = getSmallestNumberInMatrix(matrix);
  console.log("Smallest number in matrix: " + smallestNumberInMatrix);

  var currentNumber = 0;
  for (var i = 0; i < matrix.length; i++) {
    for (var j = 0; j < matrix[i].length; j++) {
      currentNumber = matrix[j][i];
      matrix[j][i] = currentNumber - smallestNumberInMatrix;
    }
  }

  HungarianAlgorithm.step1(7);

  function getSmallestNumberInMatrix(matrix) {
    var smallestNumberInMatrix = matrix[0][0];
    var currentNumber = 0;
    for (var i = 0; i < matrix.length; i++) {
      for (var j = 0; j < matrix[i].length; j++) {
        currentNumber = matrix[i][j];
        smallestNumberInMatrix = (smallestNumberInMatrix < currentNumber) ?
          smallestNumberInMatrix : currentNumber;
      }
    }
    return smallestNumberInMatrix;
  }
}

HungarianAlgorithm.step8 = function() {
  console.log("Step 8: Covering zeroes with Step 5 - 8 until Step 9 is reached");
  HungarianAlgorithm.step5();
}

HungarianAlgorithm.step9 = function(){
  console.log("Step 9...");
}


HungarianAlgorithm.step1(1);
HungarianAlgorithm.step2();
HungarianAlgorithm.step3();
HungarianAlgorithm.step4();
HungarianAlgorithm.step5();
HungarianAlgorithm.step6();
3
jjcosare

以下の手順を使用して割り当てを行います。

  • 0が1つしかない場合は行を割り当て、それ以外の場合は一時的に行をスキップします
  • 割り当てられた列の0を取り消します
  • すべての列について同じことを行います

上記の手順を使用して割り当てを行った後、以下の手順に従って、すべての0をカバーする最小行数を取得します。

  • ステップ1-割り当てられていない行にチェックマークを付ける
  • ステップ2-チェックされた行に0がある場合は、対応する列にチェックマークを付けます
  • ステップ-チェックされた列に割り当てがある場合は、対応する行にチェックマークを付けます
  • ステップ4-ティックが不可能になるまで、ステップ2と3を繰り返します。
  • ステップ5-チェックされていない行とチェックされた列に線を引きます

あなたの場合:(0-行と列のインデックス付け)

  • 行0には2つの0があるため、スキップします
  • 行1を割り当て、列2のすべての0を取り消します
  • 行2は、交差していない0が2つあるため、スキップします。
  • 交差していない0がないため、行3をスキップします
  • 行4は、交差していない0が2つあるため、スキップします。

  • 列0を割り当てます
  • 2つの交差していない0(行2と行4)があるため、列1をスキップします。
  • すでに0が割り当てられているため、列2をスキップします
  • 列3を割り当て、行2の0を取り消します。
  • 列4を割り当て、行4の0を取り消します。
  • 割り当てられた0は「_」で示され、「x」は取り消し線の付いた0を示します

(_ 1 x 1 1)、(1 1 _ 1 1)、(1 x x _ 1)、(1 1 x 1 1)、(1 x x 1 _)

  • 割り当てを行った後のマトリックスは、上記のようになります。

次に、上記の5つの手順に従って、すべての0をカバーする最小行数を取得します。

  • 行3はまだ割り当てられていないため、チェックマークを付けます
  • 行3の列2は0なので、列2にチェックマークを付けます。
  • 列2の行1に割り当てがあるため、行1にチェックマークを付けます。
  • 次に、チェックされていない行(つまり、行0、2、4)とチェックされている列(つまり、列2)を通る線を描画します。
  • これらの4行は、すべての0をカバーします

お役に立てれば:)


PS:各行と列に複数の0があるために最初の割り当てが不可能な場合、これは1つの任意の割り当てを取ることで処理できます(各行と列に複数の0が存在する場合、複数の可能な割り当てが最適なソリューションになります)

2
Yash Palriwal

@CMPSの回答は、かなりの数のグラフで失敗します。私は問題を解決する解決策を実行したと思います。

ハンガリーのアルゴリズム に関するウィキペディアの記事に従い、常に機能しているように見える実装を作成しました。ウィキペディアから、最小数の線を描画する方法は次のとおりです。

まず、できるだけ多くのタスクを割り当てます。割り当てのないすべての行をマークします。新しくマークされた行でゼロを持つすべての(マークされていない)列をマークします。新しくマークされた列に割り当てがあるすべての行をマークします。割り当てられていないすべての行に対して繰り返します。

これが私のRuby実装です:

def draw_lines grid
    #copies the array    
    marking_grid = grid.map { |a| a.dup }

    marked_rows = Array.new
    marked_cols = Array.new

    while there_is_zero(marking_grid) do 
        marking_grid = grid.map { |a| a.dup }

        marked_cols.each do |col|
            cross_out(marking_grid,nil, col)
        end

        marked = assignment(grid, marking_grid)    
        marked_rows = marked[0]
        marked_cols.concat(marked[1]).uniq!

        marking_grid = grid.map { |a| a.dup }

        marking_grid.length.times do |row|
            if !(marked_rows.include? row) then
                cross_out(marking_grid,row, nil)
            end
        end

        marked_cols.each do |col|
            cross_out(marking_grid,nil, col)
        end

    end


    lines = Array.new

    marked_cols.each do |index|
        lines.Push(["column", index])
    end
    grid.each_index do |index|
        if !(marked_rows.include? index) then
            lines.Push(["row", index])
        end
    end
    return lines
end


def there_is_zero grid
    grid.each_with_index do |row|
        row.each_with_index do |value|
            if value == 0 then
                return true
            end
        end
    end
    return false
end

def assignment grid, marking_grid 
    marking_grid.each_index do |row_index|
        first_zero = marking_grid[row_index].index(0)
        #if there is no zero go to next row
        if first_zero.nil? then
            next        
        else
            cross_out(marking_grid, row_index, first_zero)
            marking_grid[row_index][first_zero] = "*"
        end
    end

    return mark(grid, marking_grid)
end


def mark grid, marking_grid, marked_rows = Array.new, marked_cols = Array.new
    marking_grid.each_with_index do |row, row_index|
        selected_assignment = row.index("*")
        if selected_assignment.nil? then
            marked_rows.Push(row_index)
        end
    end

    marked_rows.each do |index|
        grid[index].each_with_index do |cost, col_index|
            if cost == 0 then
                marked_cols.Push(col_index)    
            end
        end
    end
    marked_cols = marked_cols.uniq

    marked_cols.each do |col_index|
        marking_grid.each_with_index do |row, row_index|
            if row[col_index] == "*" then
                marked_rows.Push(row_index)    
            end
        end
    end

    return [marked_rows, marked_cols]
end


def cross_out(marking_grid, row, col)
    if col != nil then
        marking_grid.each_index do |i|
            marking_grid[i][col] = "X"    
        end
    end
    if row != nil then
        marking_grid[row].map! {|i| "X"} 
    end
end

grid = [
    [0,0,1,0],
    [0,0,1,0],
    [0,1,1,1],
    [0,1,1,1],
]

p draw_lines(grid)
0
KNejad