從匈牙利到KM

1、前言

學習是一個痛苦的過程,讓我們養(yǎng)成了不求甚解的習慣。

2、匈牙利算法

嗯,首先網上已經有很多啦。但是我覺得很多還是摳不清楚細節(jié),于是就有了這篇文章。 匹配就是一種兩兩不相接的邊的集合,二分匹配就是二分圖的匹配,匈牙利的基礎可以隨便看一篇。

bool find(int x){
    int i,j;
    for (j=1;j<=m;j++){    //掃描每個妹子
        if (line[x][j]==true && used[j]==false)      
        //如果有曖昧并且還沒有標記過(這里標記的意思是這次查找曾試圖改變過該妹子的歸屬問題,但是沒有成功,所以就不用瞎費工夫了)
        {
            used[j]=1;
            if (girl[j]==0 || find(girl[j])) { 
                //名花無主或者能騰出個位置來,這里使用遞歸
                girl[j]=x;
                return true;
            }
        }
    }
    return false;
}
int match ()
{
      int all = 0;
      for (i=1;i<=n;i++)
      {
          memset(used,0,sizeof(used));    //這個在每一步中清空
          if find(i) all+=1;
      }
      return all;
}

以上代碼是我從一篇的鏈接中拿過來的,line數組存的是二分圖<X,Y>的邊權,不難證明從X中一個點x出發(fā)若匹配的數量增加,等價于存在一條從x出發(fā)的增廣路。算法的思想就是對X中每個點通過find來深搜增廣路。由于每次只會遞歸used為0的點。所以遞歸深度為O(n),find的復雜度是O(n2),整個算法的復雜度是O(n3)。

嗯,到這里沒什么問題,一般的blog討論到這里也結束了。但是當你仔細看find,會發(fā)現這個函數好像跟我們平時的搜索不太一樣。

bool find(int x){
    int i,j;
    for (j=1;j<=m;j++){ 
        if (line[x][j]==true && used[j]==false)  
        {
            used[j]=1;
            if (girl[j]==0 || find(girl[j])) { 
                girl[j]=x;
                return true;
            }
            //used[j]=0;  平時這里要將賦值過得used還原。
        }
    }
    return false;
}

平時搜索失敗的時候是需要把上下文還原的,也就是去掉注釋。然而一旦去掉注釋,這將是一個指數復雜度的搜索算法。于是我們有理由相信,不需要還原used恰恰是匈牙利算法對搜索的一種優(yōu)化,而為什么可以這樣優(yōu)化,這種正確性則是我們需要證明的事,也是該算法的精髓。

我們考慮一個去掉上述注釋的find函數,此時就是普通的搜索,這樣算法肯定是對的,接下來我們要證明這樣搜索的結果與注釋時是一樣的。

我們要解決的問題是<X,Y>的二分圖中,已經used的部分組成的集合稱作前綴。初始時前綴為空集。從X中一個點x0出發(fā)在圖中找增廣路徑,查找所有邊,若對應的y之前未匹配則直接返回找到解。否則將y加入前綴,并從y之前匹配的點開始繼續(xù)搜索,搜索成功則直接返回答案,否則將該點剔除前綴。

不難發(fā)現以下結論。

  1. 在搜索過程中,除了找到增廣路結束搜索。否則每個匹配的Y中的點都對應同一個X中的點。
  2. 若從一個前綴為S時從x出發(fā)搜索失敗,則前綴為S超集時從x出發(fā)也搜索失敗。

以上發(fā)現比較顯然,就不證明啦。

定理1 若從一個前綴為S時搜索到y時繼續(xù)搜索失敗,則前綴為S超集時搜索到y繼續(xù)搜索也失敗。
證明:由搜索失敗知y肯定有匹配的點。由結論1知道在搜索成功前y匹配的點始終不會變化。再根據結論2可證明。

定義1 在前綴S時從x出發(fā)搜索和前綴T時從x出發(fā)搜索時都有解或者都無解,則稱S和T在x處前綴等價。

定理2 前綴S和前綴T在x處等價,前綴T和前綴P在x處等價,則前綴S和前綴P在x處等價。
證明:由 定義1知,顯然。

定理3 前綴S和前綴T在x處等價,若P是前綴S的超集,T是P的超集。則前綴S和前綴P在x處等價,前綴T和前綴P在x處等價。
證明:
前綴S與前綴T在x處等價,知前綴S和前綴T在x處搜索都有解或者都無解。
若前綴T和前綴S在x處有解,則由結論2的逆否命題及前綴T在x處有解知前綴P在x處有解。
若前綴T和前綴S在x處無解,則由結論2及前綴S在x處無解知前綴P在x處無解。
綜上所述,則前綴S和前綴P在x處等價,前綴T和前綴P在x處等價。

定理4 在前綴集合S時從y的匹配搜索時,設所有前綴S從y的匹配出發(fā)訪問過的搜索失敗的點為Q,則S和S并Q在y的匹配處前綴等價。
證明:由于還在搜索,所以目前的匹配都是失敗的。設從前綴S開始從y的匹配訪問過的點Q中,設至少一次在第k次遞歸中出現時的點的集合為C(k)。
Q = C(0) 并 C(1) 并 C(2) ...

下面用數學歸納法證明S和S并C(0)并C(1)...并C(n)在y處前綴等價。
對于C(0)中任意一個點y0,由于S并{y0}在y0處無解,則S并{y0}的超集在y0處也無解。所以y0出現在之后搜索的任何位置,從那里搜索必然無解。這相當于C(0)中任意一個點在之后被搜索到都將得不到解,則S并C(0)和S在y處前綴等價。
設n=k時,S和S并C(0)并C(1)...并C(k)在y處等價。
n=k+1是,設D=S并C(0)并C(1)...并C(k)。以前綴D從y的匹配開始搜索。由于C(k+1)中的點都可以從C(k)中走過來且都失敗了,因此若是從其他地方搜索到C(k+1)必然是D的超集由結論2也會失敗。故前綴D和D并C(k+1)在y匹配處前綴等價,又S和D在y匹配處前綴等價。由定理2知S和D并C(k+1)在y匹配處前綴等價,即S和S并C(0)并C(1)...并C(k)并C(k+1)在y匹配處前綴等價。

由于搜索是有限的,我們知道存在m使得k>m時C(k)為空集。
Q = C(0) 并 C(1) 并 C(2) ... 并C(m)。
S并C(0)并C(1)...并C(m)和S在y處等價,有S并Q和S在y處前綴等價。

定理5 在前綴集合S時從y的匹配搜索時,設所有由深搜訪問過的搜索失敗的點的集合為Q,則S和S并Q在y的匹配處前綴等價。
證明:顯然,任何一個訪問過的點,一定是由到y的過程中某個階段失敗的。由定理4,可以把當時的前綴替換為前綴并上該前綴起搜索失敗點的集合。這些所有階段的并集是Q。則到y時前綴可以替換為S并Q。得證。

由定理5知可以把之前訪問過的點并入前綴,即used。所以加不加注釋,搜索出來的結果是一樣的。

3、KM算法

KM算法的話,就是二分圖的匹配帶權,特別注意不是求數量最多的匹配,而是求匹配中權和最大的匹配。網上很多O(n^4)實現。這里有一個比較好的O(n^3)實現。

KM算法的圖使用鄰接矩陣來表示,沒有邊的點之間加一個權值為0的邊。這樣可以保證無論怎么連都存在完美匹配。然后通過給二分圖的兩邊的點頂標,維護每條邊的權值都小于兩邊的頂標和,對于一個完備匹配,若每條邊的權值等于兩個點的頂標和,則一定是最大權匹配。因為該權和等于所有頂標和,若存在更大的權匹配,則與每條邊的權值都小于兩邊的頂標和矛盾。算法的具體細節(jié)在鏈接里已經講得比較清楚啦。

下面講一些KM的討論,若邊有負權,算法仍然成立。情況與加好零邊后給所有的邊加上最小負數的絕對值再跑算法一直。因為一定有完美匹配,多出來的值是固定的。
若二分圖兩邊點的個數不相等,這樣匹配就不能覆蓋所有頂標,二分圖的正確性不能保證,因此KM算法只能解決二分圖兩邊點數相等的情況。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • sì 支zhī茶chá 對duì 酒jiǔ,賦fù 對duì 詩shī,燕yàn子zi 對duì 鶯yīng 兒é...
    每個人的孟母堂閱讀 1,265評論 0 6
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發(fā)現,斷路器,智...
    卡卡羅2017閱讀 134,973評論 19 139
  • 一年級語文上冊生字表 生字表一(共400字) 啊(ā)愛(ài)安(ān)岸(àn)爸(bà)八(bā)巴(bā)...
    meychang閱讀 2,881評論 0 6
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語言,發(fā)作 oo-boon-too 的音。了解發(fā)音是有意...
    螢火蟲de夢閱讀 99,586評論 9 467
  • 如果你現在的日子很糟糕,但你總是抱希望于未來,覺得未來的日子會很美好,那么相信我未來并不會變得多美好,除非從今天開...
    碳哥哥閱讀 451評論 6 32