2012年7月14日土曜日

手作業でハンガリー法 Hangarian Algorithm

project euler #345 に、割り当て問題の解き方で、名前だけ聞いたことのある「ハンガリー法(Hangarian Algorithm)」ってのが使えそうなので、勉強がてらやってみた。ほぼ手作業で。

====

下の表は、問題文中のサンプル。

753183439863 
49738356379973
28763343169583
627343773959943
767473103699303

#345 は最大値を求める問題なので、それぞれの値を1000から引いて、大小関係を逆転させておく。

993947817561137 
50361743792127
713937657831417
3736572274157
233527897301697

行ごとに、最小値を行の各要素から引く。下表のように各行に少なくとも一つ 0ができる。

8568106804240 
4765904108940
2965202404140
332616186016
029466468464

同様の操作を列ごとに行う。これで行で見ても列で見ても、最低一つの 0 ができた。

8565164944240 
4762962248940
296226544140
3323220016
0047868464

上から順に行を調べて、取り消されていない 0 がただ一個だけあれば、その 0 に仮決めの印をつける(ここでは白い太字)。仮決めした 0 と同じ列の他の行に 0 がもしあれば、取り消し印(ここでは取り消し線)をつける。

8565164944240 
4762962248940
296226544140
3323220016
0047868464

同じことを列に対して行う。つまり左から順に列を調べて、取り消されていない 0 が一個だけある列ならば、その0 に仮決め印をつけて、他の列の同じ行に 0 があれば、取り消し印をつける。

(この例では、行への操作と列への操作を一巡するだけで終わったが、もしまだ仮決めにも取り消しにもなっていない 0 が残っていれば、また行の操作からやり直す。)

8565164944240 
4762962248940
296226544140
3323220016
0047868464

仮決めの 0 を含まない行に印をつける。その行に取り消しの 0 があればその列に印をつける。さらにその列に仮決めの 0 があれば、その行に印をつける。その行に取り消しの 0 が・・・って感じで、やることが無くなるまで続ける。(言葉にすると変だけど、やってみると簡単)

8565164944240
4762962248940
296226544140
3323220016
0047868464
8565164944240
4762962248940
296226544140
3323220016
0047868464
8565164944240
4762962248940
296226544140
3323220016
0047868464

印がついていない行と、印がついた列に色をつける。ただし重なっている部分が分かるようにする。ここでは重なっていない部分を橙色、重なった部分を赤の文字色にした。

8565164944240
4762962248940
296226544140
3323220016
0047868464

色がついていない部分の最小値(ここでは54)を見つけて、色がついていない値から減算し、重なっている値には加算する。色がついているけど重なっていない部分の値はそのまま。

できたら、いったん印とか色とかをクリアして、また印をつけるところからやり直す。各行、各列に一個ずつ 0 が決まったら終わり。

以下、もう一巡したところ。

8024624403700
4222421708400
24217203600
3323220070
0047868518

さらに一巡

6322922702000
2527206700
2421720360170
33232200240
0047868 688

もっかい

63229234220072
180005980
1701000288170
332322720312
0055068 760

さらに半巡して、つまり、無色の値の最小値(72)を、それぞれの無色の値から減算したところで、やっと全ての行と列に 0 が揃った。

5602202701280 
180005980
1701000288170
332322720312
0055068760

0 になっている部分の位置関係を、そっくりそのままもとの表に当てはめると、問題文どおりの値が得られる。

    863 
 383   
  343  
   959 
767    

====
euler #345 だと 15×15 のマトリクスだけど、試しに手作業(+Excel)でやってみたら、6 巡か 7 巡で、2時間以上かかったけど、まあ一応正答がでた。正直、ハンガリー法のやり方がよくわかっていなかったのでやたらと時間がかかったが、手でやった分、なんか身についた気がする。

Youtube にもハンガリー法の実演動画がたくさんあるけど、下記リンクのインドの大学の講義が、インド訛りをのぞけば一番懇切丁寧で参考になった。 Lec-16 Assignment Problem - Hungarian Algorithm

この問題は、手作業でできてしまったので、コーディングはまた今度にすることにしたけど、project euler の thread を見ると、なんだか各人各様でかなり異なるアイデアで解いている。そのうち調べて見ようと思う。特に DP を使っているやり方とか。

0 件のコメント:

コメントを投稿