KMアルゴリズム学習ノート
3455 ワード
二分図定義
図の頂点はちょうど2つの集合に分けることができ、同じ集合内の頂点間にはエッジが許されず、異なる集合の頂点にはエッジが接続されることが許される.
問題の分類最大マッチング問題:ハンガリーアルゴリズム、Hopcroft–Karpアルゴリズム 最適重みマッチング問題:Kuhn-Munkrasアルゴリズム 肝心な思想.
拡張路(augmenting path):現在マッチング結果があると仮定し、非マッチングポイントのODのセットが存在し、マッチングと非マッチングのポイントが交互に現れるパスを見つけることができ、拡張路と呼ばれる
拡張パスのマッチングと未マッチングが逆になると、マッチング数は1増加します.
KMアルゴリズム
基本思想:トップスケールを導入することによって、最適重み値マッチングを最大マッチング問題に変換する.
ステップ1:エッジウェイト値をトップ/ロッドに変換します.一般的に、初期化時には、Xセットの要素は対応するウェイトの最大値を取り、Yセットの要素は0を取ります.次の条件を満たすエッジを取り出し、weight(i,j)=label(i)+label(j);この二分図を等子図と呼ぶ
ステップ2:拡張路を探して、X 0から、X 0 Y 4を見つけます;X 1では、拡張路が見つからず、トップスケールを調整し、等しいサブマップを拡大する必要がある.拡張パスが見つからない場合、探索されたパス上のXY点については、そのパス上のX頂点セットをS、Y頂点セットをTとし、S中の点xiおよびT中にない点yjすべてについてd=min{(L(xi)+L(yj)-weight(xiyj)}を計算し、SセットのXロッドからdを減算し、TセットのYのロッドに加える.本例では、拡張路を探す過程で、X 1、Y 4、X 0の3つのノードにアクセスするので、対応するエッジはX 1 Y 0である、dは2(欲張りなエッジ選択の観点から、X 0のために新しいエッジを選択して元の二分子図のマッチングエッジを捨てることもできるし、X 1のために新しいエッジを選択して元の二分子図のマッチングエッジを捨てることもできる.なぜなら、X 0 Y 4とX 1 Y 4を同時に選択することはできないからである.これは不合法なマッチングであるため、この場合、d=min{(L(xi)+L(yj)-weight(xiyj)}の意味は、私たちは新しいエッジを選択します.このエッジはマッチングサブマップに追加され、マッチングが合法になります.このエッジによって形成されたマッチングサブマップを選択すると、元のマッチングサブマップにこの不正なエッジを加えた不正なマッチングサブマップの重みと(合法であれば最大になります)よりも小さく、すなわち重みが最大になります.このとき再びX 1のために拡幅路を探す、X 1 Y 0を得る.
ステップ3:X 2に対して拡張路を探し、探索範囲は上図の青い経路に示すように、拡張路が見つからず、等しいサブ図を拡大する必要がある.ステップ2の同じ規則に従って、辺X 0 Y 2、X 2 Y 1が加える、d=1となる.
ステップ4:新しい等しいサブマップ上で、X 2に対して再び拡張路を探す.深さ優先であれば、得られた経路がX 2 Y 0->Y 0 X 1->X 1 Y 4->Y 4 X 0->X 0 Y 2であり、このときマッチング結果を逆にすると、X 2 Y 0、X 1 Y 4、X 0 Y 2の3つのマッチングが得られる.幅優先であれば、得られるマッチング結果は、X 0 Y 4、X 1 Y 0、X 2 Y 1であり、以下の図である.
Python実現
参考資料
http://blog.sina.com.cn/s/blo...
https://blog.csdn.net/mosquit...
図の頂点はちょうど2つの集合に分けることができ、同じ集合内の頂点間にはエッジが許されず、異なる集合の頂点にはエッジが接続されることが許される.
問題の分類
拡張路(augmenting path):現在マッチング結果があると仮定し、非マッチングポイントのODのセットが存在し、マッチングと非マッチングのポイントが交互に現れるパスを見つけることができ、拡張路と呼ばれる
拡張パスのマッチングと未マッチングが逆になると、マッチング数は1増加します.
KMアルゴリズム
基本思想:トップスケールを導入することによって、最適重み値マッチングを最大マッチング問題に変換する.
ステップ1:エッジウェイト値をトップ/ロッドに変換します.一般的に、初期化時には、Xセットの要素は対応するウェイトの最大値を取り、Yセットの要素は0を取ります.次の条件を満たすエッジを取り出し、weight(i,j)=label(i)+label(j);この二分図を等子図と呼ぶ
ステップ2:拡張路を探して、X 0から、X 0 Y 4を見つけます;X 1では、拡張路が見つからず、トップスケールを調整し、等しいサブマップを拡大する必要がある.拡張パスが見つからない場合、探索されたパス上のXY点については、そのパス上のX頂点セットをS、Y頂点セットをTとし、S中の点xiおよびT中にない点yjすべてについてd=min{(L(xi)+L(yj)-weight(xiyj)}を計算し、SセットのXロッドからdを減算し、TセットのYのロッドに加える.本例では、拡張路を探す過程で、X 1、Y 4、X 0の3つのノードにアクセスするので、対応するエッジはX 1 Y 0である、dは2(欲張りなエッジ選択の観点から、X 0のために新しいエッジを選択して元の二分子図のマッチングエッジを捨てることもできるし、X 1のために新しいエッジを選択して元の二分子図のマッチングエッジを捨てることもできる.なぜなら、X 0 Y 4とX 1 Y 4を同時に選択することはできないからである.これは不合法なマッチングであるため、この場合、d=min{(L(xi)+L(yj)-weight(xiyj)}の意味は、私たちは新しいエッジを選択します.このエッジはマッチングサブマップに追加され、マッチングが合法になります.このエッジによって形成されたマッチングサブマップを選択すると、元のマッチングサブマップにこの不正なエッジを加えた不正なマッチングサブマップの重みと(合法であれば最大になります)よりも小さく、すなわち重みが最大になります.このとき再びX 1のために拡幅路を探す、X 1 Y 0を得る.
ステップ3:X 2に対して拡張路を探し、探索範囲は上図の青い経路に示すように、拡張路が見つからず、等しいサブ図を拡大する必要がある.ステップ2の同じ規則に従って、辺X 0 Y 2、X 2 Y 1が加える、d=1となる.
ステップ4:新しい等しいサブマップ上で、X 2に対して再び拡張路を探す.深さ優先であれば、得られた経路がX 2 Y 0->Y 0 X 1->X 1 Y 4->Y 4 X 0->X 0 Y 2であり、このときマッチング結果を逆にすると、X 2 Y 0、X 1 Y 4、X 0 Y 2の3つのマッチングが得られる.幅優先であれば、得られるマッチング結果は、X 0 Y 4、X 1 Y 0、X 2 Y 1であり、以下の図である.
Python実現
import numpy as np
#
adj_matrix = build_graph() # np array with dimension N*N
#
label_left = np.max(adj_matrix, axis=1) # init label for the left set
label_right = np.zeros(N) # init label for the right set
#
match_right = np.empty(N) * np.nan
#
visit_left = np.empty(N) * False
visit_right = np.empty(N) * False
slack_right = np.empty(N) * np.inf
# ,
def find_path(i):
visit_left[i] = True
for j, match_weight in enumerate(adj_matrix[i]):
if visit_right[j]: continue # ( )
gap = label_left[i] + label_right[j] - match_weight
if gap == 0:
#
visit_right[j] = True
if np.isnan(match_right[j]) or find_path(match_right[j]): ## j , j , j
match_right[j] = i
return True
else:
#
if slack_right[j] < gap: slack_right[j] = gap
return False
# KM
def KM():
for i in range(N):
#
slack_right = np.empty(N) * np.inf
while True:
#
visit_left = np.empty(N) * False
visit_right = np.empty(N) * False
#
if find_path(i): break
# ,
# (1) X label d
# (2) Y label d
d = np.inf
for j, slack in enumerate(slack_right):
if not visit_right[j] and slack < d:
d = slack
for k in range(N):
if visit_left[k]: label_left[k] -= d
for n in range(N):
if visit_right[n]: label_right[n] += d
res = 0
for j in range(N):
if match_right[j] >=0 and match_right[j] < N:
res += adj_matrix[match[j]][j]
return res
参考資料
http://blog.sina.com.cn/s/blo...
https://blog.csdn.net/mosquit...