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