二分圖最大匹配:匈牙利算法的python實現


二分圖匹配是很常見的算法問題,一般用匈牙利算法解決二分圖最大匹配問題,但是目前網上絕大多數都是C/C++實現版本,沒有python版本,於是就用python實現了一下深度優先的匈牙利算法,本文使用的是遞歸的方式以便於理解,然而迭代的方式會更好,各位可以自行實現。

1、二分圖、最大匹配

什么是二分圖:二分圖又稱作二部圖,是圖論中的一種特殊模型。 設G=(V,E)是一個無向圖,如果頂點V可分割為兩個互不相交的子集(A,B),並且圖中的每條邊(i,j)所關聯的兩個頂點i和j分別屬於這兩個不同的頂點集(i in A,j in B),則稱圖G為一個二分圖。 

什么是匹配:把上圖想象成3位工人和4種工作,連線代表工人願意從事某項工作,但最終1個工人只能做一種工作,最終的配對結果連線就是一個匹配。匹配可以是空。 
什么是最大匹配:在願意從事的基礎上,能夠最多配成幾對。 

現在要用匈牙利算法找出最多能發展幾對。 
[color=green][size=medium] 
匈牙利算法是解決尋找二分圖最大匹配的。

更多二分圖最大匹配的圖解可以參考 http://xuxueliang.blog.51cto.com/5576502/1297344

以下是代碼,為了圖省事使用了類,實際上並不需要這樣

M=[]
class DFS_hungary():

    def __init__(self, nx, ny, edge, cx, cy, visited):
        self.nx, self.ny=nx, ny
        self.edge = edge
        self.cx, self.cy=cx,cy
        self.visited=visited

    def max_match(self):
        res=0
        for i in self.nx:
            if self.cx[i]==-1:
                for key in self.ny:         # 將visited置0表示未訪問過
                    self.visited[key]=0
                res+=self.path(i)return res

    def path(self, u):
        for v in self.ny:
            if self.edge[u][v] and (not self.visited[v]):
                self.visited[v]=1
                if self.cy[v]==-1:
                    self.cx[u] = v
                    self.cy[v] = u
                    M.append((u,v))
                    return 1
                else:
                    M.remove((self.cy[v], v))
                    if self.path(self.cy[v]):
                        self.cx[u] = v
                        self.cy[v] = u
                        M.append((u, v))
                        return 1
        return 0

ok,接着測試一下:

if __name__ == '__main__':
    nx, ny = ['A', 'B', 'C', 'D'], ['E', 'F', 'G', 'H']
    edge = {'A':{'E': 1, 'F': 0, 'G': 1, 'H':0}, 'B':{'E': 0, 'F': 1, 'G': 0, 'H':1}, 'C':{'E': 1, 'F': 0, 'G': 0, 'H':1}, 'D':{'E': 0, 'F': 0, 'G': 1, 'H':0}} # 1 表示可以匹配, 0 表示不能匹配
    cx, cy = {'A':-1,'B':-1,'C':-1,'D':-1}, {'E':-1,'F':-1,'G':-1,'H':-1}
    visited = {'E': 0, 'F': 0, 'G': 0,'H':0}

    print DFS_hungary(nx, ny, edge, cx, cy, visited).max_match()

結果為4,是正確的。各位也可以使用其它二分圖來測試。

---------------------------------------------------------補充BFS版本匈牙利算法-------------------------------------------------------

  BFS版本的匈牙利算法性能更好一些,但是比較難理解,下面把BFS版本的算法也貼出來,也是翻譯自c++版本,這次使用更好的迭代方式替換了遞歸方式

def BFS_hungary(g,Nx,Ny,Mx,My,chk,Q,prev):
    res=0
    for i in xrange(Nx):
        if Mx[i]==-1:
            qs=qe=0
            Q[qe]=i
            qe+=1
            prev[i]=-1

            flag=0
            while(qs<qe and not flag):
                u=Q[qs]
                for v in xrange(Ny):
                    if flag:continue
                    if g[u][v] and chk[v]!=i:
                        chk[v]=i
                        Q[qe]=My[v]
                        qe+=1
                        if My[v]>=0:
                            prev[My[v]]=u
                        else:
                            flag=1
                            d,e=u,v
                            while d!=-1:
                                t=Mx[d]
                                Mx[d]=e
                                My[e]=d
                                d=prev[d]
                                e=t
                qs+=1
            if Mx[i]!=-1:
                res+=1
    return res

測試一下:

if __name__ == '__main__':
       g=[[1,0,1,0],[0,1,0,1],[1,0,0,1],[0,0,1,0]]
       Nx=4
       Ny=4
       Mx=[-1,-1,-1,-1]
       My=[-1,-1,-1,-1]
       chk=[-1,-1,-1,-1]
       Q=[0 for i in range(100)]
    prev=[0,0,0,0]
print BFS_hungary()

結果為4,正確

 


免責聲明!

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



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