二分圖匹配--匈牙利算法


二分圖匹配--匈牙利算法

基本定義:

二分圖 —— 對於無向圖G=(V,E),如果存在一個划分使V中的頂點分為兩個互不相交的子集,且每個子集中任意兩點間不存在邊 ϵ∈E,則稱圖G為一個二分圖。

二分圖的充要條件是,G至少有兩個頂點,且所有回路長度為偶數。

匹配 —— 邊的集合,其中任意兩條邊都不存在公共頂點。
匹配邊即是匹配中的元素,匹配點是匹配邊的頂點,同樣非匹配邊,非匹配點相反定義。

最大匹配——在圖的所有匹配中,包含最多邊的匹配成為最大匹配
完美匹配——如果在一個匹配中所有的點都是匹配點,那么該匹配稱為完美匹配。

附注:所有的完美匹配都是最大匹配,最大匹配不一定是完美匹配。假設完美匹配不是最大匹配,那么最大匹配一定存在不屬於完美匹配中的邊,而圖的所有頂點都在完美匹配中,不可能找到更多的邊,所以假設不成立,及完美匹配一定是最大匹配。

交替路——從一個未匹配點出發,依次經過非匹配邊,匹配邊,非匹配邊…形成的路徑稱為交替路,交替路不會形成環。

增廣路——起點和終點都是未匹配點的交替路。
因為交替路是非匹配邊、匹配邊交替出現的,而增廣路兩端節點都是非匹配點,所以增廣路一定有奇數條邊。而且增廣路中的節點(除去兩端節點)都是匹配點,所屬的匹配邊都在增廣路徑上,沒有其他相連的匹配邊,因此如果把增廣路徑中的匹配邊和非匹配邊的“身份”交換,就可以獲得一個更大的匹配(該過程稱為改進匹配)。

示例圖

enter description here

Fig1_09_09.JPG
enter description here
enter description here

注釋

  • Fig3是一個二分圖G=(V,E),V={1,2,3,4,5,6,7,8},E={(1,7),(1,5),(2,6),(3,5),(3,8),(4,5),(4,6)},該圖可以重繪成Fig4,V可分成兩個子集V={V1,V2},V1={1,2,3,4},V2={5,6,7,8}。

  • Fig4中的紅色邊集合就是一個匹配{(1,5),(4,6),(3,8)}

  • Fig2中是最大匹配

  • Fig1中紅色邊集合是完美匹配

  • Fig1中交替路舉例(4-6-2-7-1-5)

  • Fig4中增廣路(2-6-4-5-1-7)

匈牙利樹

匈牙利樹中從根節點到葉節點的路徑均是交替路,且匈牙利樹的葉節點都是匹配點。
匈牙利算法
求解最大匹配的算法,通過不斷的尋找增廣路徑,並將增廣路徑進行改進匹配,直至找不到更多的增廣路徑。
二分圖的最大匹配可以通過匈牙利樹的搜索尋找增廣路徑來獲得,而樹的搜索可以使用深度優先搜索(DFS)或者廣度優先搜索(BFS)
下面使用matlab代碼實現DFS和BFS下的匈牙利算法:

  1. function Hungarian(c1, m, IsDraw) 
  2. % 匈牙利算法尋找無向二分圖的最大匹配 
  3. % inputs: 
  4. % -AdjTable 頂點元素的鄰接表,cell結構,每一個元素是一個一維數組, 
  5. % 保存對應節點的鄰接節點編號 
  6. % -c1 第一個頂點子集元素個數 
  7. % -m 搜索方法,'B'是廣度優先搜索,‘D’是深度優先搜索 
  8. % -IsDraw 是否繪圖 
  9.  
  10. if nargin<2 
  11. m='B'
  12. IsDraw=0
  13. elseif nargin<3 
  14. IsDraw=0
  15. end 
  16.  
  17. global MatTable Check AdjTable 
  18. % -MatTable 匹配表,長度為頂點個數,每個元素存放該節點所在匹配邊的另一端節點 
  19. % 的編號;如果是非匹配點,則對應值為0 
  20. cn=length(AdjTable);%頂點個數 
  21. MatTable=zeros(1,cn);% 默認都是未匹配點 
  22. Check = zeros(1,cn); % 覆蓋過的點不能再訪問,否則死循環 
  23.  
  24. EdgesNum=0;% 最大匹配中元素個數 
  25. if m=='D' % 深度優先搜索 
  26. if c1<cn-c1 
  27. for i=1:c1 
  28. if DFS(i
  29. EdgesNum=EdgesNum+1
  30. end 
  31. end 
  32. else 
  33. for j=c1+1:cn 
  34. if DFS(j
  35. EdgesNum=EdgesNum+1
  36. end 
  37. end  
  38. end 
  39. else % 廣度優先搜索 
  40. EdgesNum = BFS(c1);  
  41. end 
  42. fprintf('There is %f edges in the biggest matches.\n',EdgesNum); 
  43. if IsDraw 
  44. c2=cn-c1; 
  45. X1=ones(c1,1)*40
  46. Y1=20:10:c1*10+19
  47. X2=ones(c2,1)*100
  48. Y2=20:10:c2*10+19
  49. X=[X1;X2]
  50. Y=[Y1';Y2']
  51. color={'ro:','ko:'}
  52. for i=1:cn 
  53. if MatTable(i)~=0 
  54. plot(X(i),Y(i),color{1},'MarkerSize',15);hold on  
  55. if i<=c1 
  56. text(X(i)-3,Y(i),num2str(i)); 
  57. else 
  58. text(X(i)+3,Y(i),num2str(i)); 
  59. end 
  60. else 
  61. plot(X(i),Y(i),color{2},'MarkerSize',15);hold on 
  62. if i<c1 
  63. text(X(i)-3,Y(i),num2str(i)); 
  64. else 
  65. text(X(i)+3,Y(i),num2str(i)); 
  66. end 
  67. end 
  68. end 
  69. for i=1:cn 
  70. for j=1:length(AdjTable{i}
  71. if MatTable(i)==AdjTable{i}(j
  72. plot(X([i,AdjTable{i}(j)]),Y([i,AdjTable{i}(j)]),'r.-','LineWidth',2);hold on 
  73. else 
  74. plot(X([i,AdjTable{i}(j)]),Y([i,AdjTable{i}(j)]),'k.-','LineWidth',2);hold on 
  75. end 
  76. end 
  77. end 
  78. box('on'
  79. axis off 
  80. hold off 
  81.  
  82. end 
  83.  
  84.  
  85. end 
  86. % AdjTable 鄰接表 
  87. % MatTable 匹配表 
  88. function bool=DFS(u) 
  89. % n 是左側未匹配點的編號 
  90. % 尋找節點n的一條未匹配邊 
  91. global AdjTable MatTable Check 
  92. for i=1:length(AdjTable{u}
  93. v=AdjTable{u}(i); 
  94. if ~Check(v) 
  95. Check(v)=1
  96. if MatTable(v) == 0|| DFS(MatTable(v))  
  97. %v是未匹配點則找到增廣路徑,交換身份 
  98. %否則,如果v的匹配點存在增廣路徑, 
  99. %那么也是找到一條增廣路徑 
  100. % || 是短路運算符 
  101. MatTable(u) = v; 
  102. MatTable(v) = u; 
  103. bool=1
  104. return;  
  105. end  
  106. end 
  107. end 
  108. bool=0
  109. end 
  110.  
  111. function EdgeNum=BFS(c1) 
  112. global AdjTable MatTable Check 
  113. pre = zeros(length(AdjTable))-1
  114. % 存放的是該點所在的非匹配邊的前一個非匹配邊的右端端點編號 
  115. queue=[];% 廣度優先搜索需要的搜索隊列 
  116. EdgeNum=0;%最大匹配元素個數 
  117. for i=1:c1 
  118. if ~MatTable(i) % 尋找未匹配點 
  119. queue=[i];%入隊列 
  120. flag = 0; % 未找到增廣路徑 
  121. pre(i)=-1; % 為了最后改進路徑時設定終點  
  122. while(~isempty(queue)&&~flag) 
  123. u=queue(1); 
  124. queue(1)=[];%出隊列 
  125. edges=AdjTable{u}
  126. for j=1:length(edges) 
  127. v = edges(j); 
  128. if ~flag && Check(v)~=i 
  129. Check(v)=i
  130. queue=[queue,MatTable(v)]
  131. %找到一條匹配路,將匹配路的右端節點放入隊列 
  132. if MatTable(v) % 非增廣路 
  133. pre(MatTable(v))=u; 
  134. %下一條非匹配邊的起點對應前一條非匹配邊的起點 
  135. else % 找到增廣路徑 
  136. flag=1
  137. d=u; 
  138. e=v; 
  139. while d~=-1 
  140. t = MatTable(d); 
  141. MatTable(d)=e; 
  142. MatTable(e)=d; 
  143. d = pre(d); 
  144. e = t; 
  145. end 
  146. end 
  147. end 
  148. end 
  149. end 
  150. end 
  151. if MatTable(i)~=0 %表示找到增廣路徑了,此時起點肯定在匹配邊上 
  152. EdgeNum=EdgeNum+1
  153. end 
  154. end 
  155. end 
  156.  
  157.  
  158. function testHungarian 
  159. c1=5
  160. c2=5
  161. % AdjMatrix=randi(2,c1,c2)-1; 
  162. t=0.7
  163. AdjMatrix=rand(c1,c2)>t; 
  164. % AdjMatrix=ones(c1,c2); 
  165. global AdjTable 
  166. AdjTable = cell(c1+c2,1); 
  167. for i=1:c1 
  168. t=find(AdjMatrix(i,:)~=0); 
  169. AdjTable{i}=c1+t; 
  170. end 
  171. for j=1:c2 
  172. t=find(AdjMatrix(:,j)~=0); 
  173. AdjTable{c1+j}=t; 
  174. end 
  175.  
  176. Hungarian(c1,'B',1); 
  177. end 
  178.  

分析
參考的blog中指出算法的時間復雜度為,實際應用中使用BFS的算法比DFS算法更快,但是在matlab代碼中,發現使用DFS算法的搜索比BFS算法搜索的速度快不少,尤其是頂點和邊數比較大的情形。


參考文獻
Renfei Song's Blog
百度百科-二分圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM