【算法笔记】稳定婚姻匹配问题-GS算法
什么是稳定(婚姻)匹配问题
这里是百度百科。有N男N女,均为异性恋,每个人都对异性有好感度排序。如何将他们两两配对,才能尽可能使结果令每个人都满意。
当然也有N男M女、多对一的情况,这里先不讨论(网上有些大牛写了论文)。
处理方案
被广泛认可的算法是由美国数学家 David Gale 和 Lloyd Shapley 于1962年发明的 Gale-Shapley算法,简称GS算法。GS算法的思路如下:
先给N男N女从0~N-1分别编号,然后要求每个人写出他们对异性的好感度排序。为了方便,这里N取4.比如0号男最喜欢3号女,其次是0号女,再次是1号女,最后是2号女,则0号男给出的排序为3 0 1 2.
下图中展示了好感排序情况(随机给出):
第一轮,先让每个男性向他们最喜欢的女性表白(也可以女性表白男性,同理)。例如0号男会先向3号女表白。如果一个女性同时收到了两个表白,她会选择接受自己最喜欢的那个男性。如3号女同时收到0号和2号的表白,接受0号。
第一次匹配结束后的情况:
0号男-3号女 1号男-0号女 3号男-1号女
第二轮,由于第一轮时只有2号男落单(惨),这次2号男选择向自己第二喜欢的1号女表白。1号女接到表白后,会比较现任对象(3号男)和2号男,发现自己更喜欢2号男,于是与3号男分手,投入2号男的怀抱。
第二次匹配结束后的情况:
0号男-3号女 1号男-0号女 2号男-1号女
第三轮,3号男向自己第二喜欢的0号女表白,0号女比较现任配偶(1号男)和3号男,觉得自己还是更喜欢现任配偶,3号男被残忍拒绝。这次匹配关系没有发生变化。
第四轮,3号男向自己第三喜欢的2号女表白。2号女还没有收到过表白,直接接受。
第四次匹配结束后的情况:
0号男-3号女 1号男-0号女 2号男-1号女 3号男-2号女
此时每个人都找到了配偶,匹配结束。
简单总结一下,每一轮开始时:
①没有配偶的男性选择自己好感度最高而且没表白过的女性表白。
②接到表白的女性比较现任配偶和新追求者的好感度,选择与好感度更高的男性重新匹配。
GS算法的局限性
从算法过程容易看出,按照“男性表白女性”的策略对女性更有利,因为女性的配偶质量会不断提升,而男性的配偶质量会不断变差。相反则对男性有利。不同的表白策略得出的匹配结果很可能是不同的。
GS算法能否保证每个人都有对象
假设有一个男性A落单,由于男女人数相等,则必定还有一个女性B落单。
根据算法,B是被动接受表白,只要收到过一次表白,B就不可能落单。
而如果A没有配偶,他会一直表白下去,将N个女性全部表白一遍。这个过程中B一定会收到表白,两人匹配。
上述内容同时可以证明,该算法会在有限次运算后结束(运算次数小于n*n)。
GS算法的结果是否稳定
“稳定”是指:算法结束后,如果再进行一轮表白,一定不会出现男女双方的选择都更优的情况。
这一点容易证明。根据算法局限性,再进行表白,男性对新配偶的好感度只会下降。
假设男性A与女性B本应匹配(也就是说现在两个人分别有自己的配偶,但是从好感度角度来说,他们都更希望彼此形成新的匹配),可以分两种情况讨论:
1.男性A曾对女性B表白,但此时两人没有匹配,说明女性B后来遇到了好感度更高的男性,假设不成立。
2.男性A不曾对女性B表白,说明男性A的配偶好感度排在女性B前面,假设不成立。
代码实现
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<iostream> 5 #include<cstring> 6 #include<string> 7 using namespace std; 8 const int MAXN = 0 + 5; 9 int Num,nowMatch; 10 int Match_A[MAXN], Match_B[MAXN], Preference_A[MAXN][MAXN], Preference_B[MAXN][MAXN], Now[MAXN]; 11 //Match数组记录匹配情况,Preference[i][1..n]记录好感排序 12 inline void init() 13 {//读入、预处理 14 scanf("%d", &Num); 15 for (int i = 0; i < Num; ++i) 16 for (int j = 0; j < Num; ++j) 17 scanf("%d", &Preference_A[i][j]); 18 for (int i = 0; i < Num; ++i) 19 for (int j = 0; j < Num; ++j) 20 scanf("%d", &Preference_B[i][j]); 21 for (int i = 0; i < Num; ++i) 22 Match_A[i] = -1, Match_B[i] = -1; 23 } 24 bool Compare(int a, int b, int Obj) 25 {//比较追求当前的B[i]的两个人在B[i]处的好感排序 26 int index_a = -1, index_b = -1; 27 for (int i = 0; i < Num; ++i) 28 { 29 if (Preference_B[Obj][i] == a) 30 index_a = i; 31 else if (Preference_B[Obj][i] == b) 32 index_b = i; 33 } 34 if (index_a < index_b) return true; 35 return false; 36 } 37 bool AllMatch() 38 {//判断是否完成匹配 39 int count = 0; 40 for (int i = 0; i < Num; ++i) 41 if (Match_A[i] != -1) 42 count++; 43 if (count < Num) return false; 44 return true; 45 } 46 void Gale_Shapley() 47 { 48 while (! AllMatch()) 49 {//算法会在所有人完成匹配后终止 50 for (int i = 0; i < Num; i ++) 51 { 52 if (Match_A[i] != -1) continue; 53 nowMatch = Match_B[Preference_A[i][Now[i]]]; 54 //nowMatch是A[i]想要表白的B[i]目前的对象,没有则为-1 55 if (nowMatch == -1 || Compare(i, nowMatch, Preference_A[i][Now[i]])) 56 {//如果B[i]没有对象,或者B[i]对A[i]更有好感,则A[i]和B[i]匹配 57 Match_A[i] = Preference_A[i][Now[i]]; 58 Match_A[nowMatch] = -1; 59 Match_B[Preference_A[i][Now[i]]] = i; 60 } 61 Now[i] ++; 62 } 63 } 64 } 65 inline void Output() 66 { 67 for (int i = 0; i < Num; ++i) 68 { 69 printf("A-%d is matched to B-%d\n", i, Match_A[i]); 70 } 71 } 72 int main() 73 { 74 init(); 75 Gale_Shapley(); 76 Output(); 77 return 0; 78 }
测试一下最初给的那组数据
应用
虽然算法解决的问题是“稳定婚姻匹配”,但是真的按照这个算法来选对象岂不太简单粗暴了…不过这好歹还是对象包分配
GS算法可以用于解决工作岗位的分配问题,例如某公司有n个岗位,m(m>=n)人竞争,可以根据公司和应聘者对彼此的好感度排序,利用算法算出最合适的人选。
漫谈
如果把上文中的人视为点,好感关系视为线,就构成了一个二分图匹配问题。问题就可以转化为:根据已有的好感度排序,如何连线才能使得左右两侧的点一一相连,且连线结果尽量令人满意。
匹配成功后的结果如图所示,是一个一一映射:
如果把这个问题削弱一下,条件可以改为:每个人都对M(M<=N)名异性有好感,这种好感不分大小。如何使尽量多对互有好感的人相匹配。这个问题称作二分图最大匹配问题,可以用匈牙利算法求解。