听班助在课上给我们讲的一个算法,挺有趣的,一开始不是很理解,在网上看了一些资料后,就差不多了,做个笔记,记录下来吧,这也是我第一次写算法的博客笔记。
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; }