GS穩定匹配算法算法


     聽班助在課上給我們講的一個算法,挺有趣的,一開始不是很理解,在網上看了一些資料后,就差不多了,做個筆記,記錄下來吧,這也是我第一次寫算法的博客筆記。

 

GS算法簡介:

     算法是為了解決“穩定匹配難題(Stable Matching Problem)”而提出的,所謂穩定匹配難題,是指: 有n個男人,還有n個女人,男人心目中有自己的心上人列表,從最喜歡的女神一直排列下去,而女人心中也有相同的列表。很明顯的,某男喜歡的女人,她可能根本看不上他。而多個女人喜歡的男人,也不可能同時娶這些女人。所以要找出一個讓所有人都能結婚,且大家都滿意的方案是很難的。

 
        

    1962 年,美國數學家 David Gale 和 Lloyd Shapley 發明了一種尋找穩定婚姻的策略。不管男女各有多少人,不管他們各自的偏好如何,應用這種策略后總能得到一個穩定的婚姻搭配。這一個算法在2012年獲得了諾貝爾經濟學獎。(參考http://zhidao.baidu.com/question/494779153.html

 
算法的步驟是:

第一步,是由男人們一輪一輪的去找他最喜歡的女人表白,女人可以選擇接受,也可以選擇拒絕。 此時女人會遇到三種情況,第一是沒人來表白;第二是有一個人來表白;第三是有多個人來表白。女人的策略是,如果沒人來表白就再等一輪,如果有一個人來表白就暫時同他交往着,如果有多個人來表白,就同最喜歡的那個交往。一輪過后,有些男人有女友了,有些還打着光棍。

 

第二步,那些光棍們重復上一輪的行為,從還沒被拒絕的女生中,找自己最心儀的女人表白。女人會遇到一個特殊情況,自己有男友了,但是又有一個男人來表白。如果新來的男人比現在的差,她就忽略,如果比現在的好,那就劈腿換新人。

 

第三步再重復第二步的行為。如此循環往復,一直到最后每個人都成功匹配為止。Shapley和Gale兩人從數學上證明了,這種策略的結果是最穩固的。

 
        
證明:

穩定匹配要證明兩個問題,一個穩定的匹配也必須滿足下面的條件:

①匹配是完美的。即每個男人都會找到自己最終的女神,每個女人都會找到自己的男人,不存在有人是單身。

②不包含不穩定因素。

(不穩定因素:有兩對夫妻(M1 F2),(M2 F1)。M1心目中更喜歡F1,但是他和F2結婚了,M2心目中更喜歡F2,但是命運卻讓他和F1結婚了,顯然這樣的婚姻是不穩定的,隨時都可能發生M1和F1私奔或者M2和F2私奔的情況。所以在做出匹配選擇的時候(也就是結婚的時候),我們需要做出穩定的選擇,以防這種情況的發生。)

 
        

證明問題①(用反證法):隨着輪數的增加,總有一個時候所有人都能配上對。因為男生根據自己心目中的排名依次對女士進行表白,假如有一個人沒有配上對,那么這個人必定是向所有的女孩進行表白了。但是女孩只要被表白過一次,女孩就不可能是單身,也就是說此時所有的女生都不是單身的,這與有一個人沒有配上對是相悖的,男孩女孩人數相等。所以假設不成立。該算法一定會使得所有人都能夠配對成功,即為完美匹配。 

 
        

證明問題②(也是反證法):隨着輪數的增加,男士追求的對象越來越糟,而女士的男友則可能變得越來越好。假設男A和女1各有各自的對象,但是比起現在的對象,男A更喜歡女1,所以,在此之前男A肯定已經跟女1表白過的,並且女1拒絕了男A,也就是女1有了比男A更好的男友,不會出現私奔的情況,這樣就不會包含穩定因素。

 
        

由①②證明了這個的算法的正確性(參考http://blog.csdn.net/cscmaker/article/details/8291131

 
該算法的偽代碼
   1:  while  存在男人m是自由的且還沒對每個女人都求過婚  
   2:        選擇這個男人m  
   3:                  令w是m的優先表中還沒求過婚的最高排名的女人  
   4:          if  w是自由的    
   5:              (m,w)變成約會狀態  
   6:          else  w當前與m1約會  
   7:                if  w更偏愛m1而不愛m  
   8:                      m保持自由  
   9:                else    w更偏愛m而不愛m1  
  10:                     (m,w)變成約會狀態  
  11:                      m1變成自由  
  12:                endif  
  13:                    endif endwhile 

借鑒大神的代碼

#include<iostream>
#include <stack>

using namespace std;

#define NUM 4
#define NIL -1

int GetPositionFromLaday(int ladayArray[][NUM], int laday, int man)
{
    for(int i=0; i<NUM; i++)
        if(ladayArray[laday][i] == man)
            return i;
    return NIL;
}

void ChoosePartener(stack<int>& manStack, int manPos, int manArray[][NUM], int ladayArray[][NUM], int manPerfer[], int manStartPos[], int ladayNow[])
{
    //選擇自己名單上排在首位的女人
        int perferLaday = manArray[manPos][manStartPos[manPos]];
        //如果該女孩沒有接受過表白,則接受該男孩的表白
        if(ladayNow[perferLaday] == NIL)
        {
            ladayNow[perferLaday] = manPos;
            manPerfer[manPos] = perferLaday;
        }
        //如果已經有人向她表白,則判斷其現在擁有的有沒有現在追求的好
        else
        {
            int oldPos = GetPositionFromLaday(ladayArray, perferLaday, ladayNow[perferLaday]);
            int newPos = GetPositionFromLaday(ladayArray, perferLaday, manPos); 
            if(oldPos < newPos)
            {
                manStartPos[manPos]++;//說明該女生更喜歡現在擁有的,選心目中第二位
                //加入單身行列
                manStack.push(manPos);
            }
            else //換男友
            {
                //被甩的男友恢復自由身份
                manStartPos[ladayNow[perferLaday]]++;
                //加入單身行列
                manStack.push(ladayNow[perferLaday]);
                //將追求的男士改為現任男友
                ladayNow[perferLaday] = manPos;
                manPerfer[manPos] = perferLaday;
            }
        }
}

int main()
{
    int manArray[NUM][NUM] ={{2,3,1,0},{2,1,3,0},{0,2,3,1},{1,3,2,0}};    
    int ladayArray[NUM][NUM] = {{0,3,2,1},{0,1,2,3},{0,2,3,1},{1,0,3,2}};

    int manPerfer[NUM] = {0};//每位男生選中的女生
    int manStartPos[NUM] = {0};//記錄每位男生選取的是心目中第幾位的女生
    int ladayNow[NUM] = {NIL,NIL,NIL,NIL};//女生對應的男生

    stack<int> manStack; // 還處於單身的男士

    //進行第一輪迭代,每個男生都選擇自己名單上排在首位的女生。
    for(int pos=0; pos<NUM; pos++)
    {
        ChoosePartener(manStack, pos, manArray, ladayArray, manPerfer, manStartPos,ladayNow);
    }

    while(manStack.size()!=0)
    {
        int manPos = manStack.top();
        manStack.pop();
        ChoosePartener(manStack, manPos, manArray, ladayArray, manPerfer, manStartPos,ladayNow);
    }

    for(int i =0;i<NUM; ++i)
        cout<<"Man NO.: "<<i<<" Laday NO.: "<<manPerfer[i]<<endl;
}
View Code


免責聲明!

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



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