稳定匹配问题
稳定匹配问题(stable matching
)是一个常见的问题,GS算法是解决稳定匹配问题的一个优秀的算法。下面,我将以男女配对的例子来介绍稳定匹配问题并阐述GS算法的具体步骤。GS算法,全称Gale-Shapley算法
。学习完稳定匹配问题和整个算法流程之后,我觉得它还可以起另外一个别名,Get-rid-of-Single算法,单身狗脱单算法。
问题描述
有n
只男性单身狗的集合M = {m1, m2, ..., mn}
和n
只女性单身狗的集合W = {w1, w2, ..., wn}
。假设每只男性单身狗对n
只女性单身狗的喜好程度都不同,每只女性单身狗对n
只男性单身狗亦如是。男单身狗mi(1 <= i <= n)
有一张属于自己的关于对面n
只女单身狗的排序表,mi
把他最爱慕的女性放在第一位,第二爱慕的女性放在第二位,以此类推,排名越靠前女性表示mi
越爱慕的女性。同样地,女单身狗wi(1 <= i <= n)
也有一张属于自己的关于对面n
只男单身狗的排序表,排名越靠前的男性越受wi
的喜爱。每只单身狗都希望与自己最喜爱的对象结为侠侣,浪迹江湖。现在,有一个名唤月老的NPC在为这2*n
只单身狗牵红线,月老收集了这些单身狗各自的排序表,并根据他们的排序表来牵红线让这些单身狗结成n
对侠侣,使得这n
对侠侣达成一个稳定匹配,和谐地浪迹江湖。
【稳定匹配】假设有两对伴侣(m1, w1)
、(m2, w2)
,在m1
的排序表中w2
的排名比w1
高,也就是说,m1
喜欢w2
比喜欢他现任w1
要多一点。此时,若w2
正好喜欢m1
比喜欢她现任m2
要多一点,那么m1
和w2
就很有可能背叛他们目前各自的侠侣关系重新与更喜欢的对象结为侠侣,剩下被甩的w1
和m2
继续沦落为单身狗。上面这种情况我们称为不稳定因素,要是一个匹配之中没有任何不稳定因素,那么这个匹配称为稳定匹配。再举个例子,同样是(m1, w1)
、(m2, w2)
,m1
更喜欢w2
,但是w2
不喜欢m1
。此时,这个匹配是稳定的。因为m1
和w2
并非相互之间都更喜欢对方,因此他们不会”私奔”,不会打破现有的匹配关系。这样一个匹配,虽然m1
无法得到自己最喜欢的w2
,但这个匹配的关系是和谐稳定的。因此,我们定义稳定匹配的概念如下:给定的一组匹配结果里面,n
对侠侣之间任何两对侠侣都不会存在有人想”私奔”的不稳定因素。
输入输出
输入: 每个男单身狗对n
个女单身狗的排序表,每个女单身狗对n
各男单身狗的排序表
输出: n
对满足稳定匹配的伴侣
算法基本思想
初始,每个人都是单身狗,分别根据自己对异性的排序表开始找对象。假设一只男性单身狗mi
选择了他的排序表上排名最高的女性w
,并且向她示爱。这个时候就可能存在下面3种情况:
- 【1】
w
也是单身狗,于是他们两个结为侠侣,成功脱单 - 【2】
w
已经和某男性mj
脱单,但是w
更喜欢mi
,于是w
把mj
甩了,重新和mi
结为侠侣。mi
成功挖到墙脚,换mj
变成单身狗 - 【3】
w
已经和某男性mj
脱单,而且w
不喜欢mi
,于是mi
挖墙脚失败,为了脱单只能继续寻找排名表上下一个喜欢的女性示爱
对于每个单身狗都重复上述的过程,不断地去”骚扰”排名表上的女性,找到还没脱单的就一起脱单,找到脱单的就挖墙脚,挖不动就找下一个喜欢的对象继续重复上述过程。这就是”伟大”的单身狗脱单(GS)算法。
算法的伪代码如下所示:
1 | 初始化所有M和W集合中元素为单身狗 |
算法分析
仔细分析一下这个单身狗脱单算法我们可以发现它具备下面几个特性:
某只女单身狗w
从第一次跟别人组成侠侣之后,如果某个男单身狗m
继续向她示爱,而且m
刚好在w
的排序表上的排名比w
的现任更高,那么w
会甩了现任然后与m
“私奔”。如果m
在w
的排序表上的排名比w
的现任低,那么w
不理睬m
,继续和现任保持关系。这个规律可以看出,w
自从第一个跟别人组成侠侣之后,她如果后面还有与其他人组成侠侣,那么跟她组成侠侣的人只会”越来越好”,即越来越符合她的排序表,也就是说,她得到的异性质量会越来越好。
某只男单身狗向他排序表上的女性示爱,第一个示爱失败之后只能找第二个,再失败再找第三个,以此类推。于是这个那单身狗在他脱单之前,他能选择的女性只会越来越不符合他的排序表,也就是说,他能选择的异性质量会越来越差。
这个算法在执行结束之后会返回一个稳定的匹配。为什么呢?因为该脱单的都脱单了,能挖动的墙脚也都被挖了,最后组成的匹配结果中,任何两对侠侣之间不会再存在任何能够挖墙脚私奔的不稳定因素了。
乍一看GS算法好像是偏爱女性的一种算法。但实际上,GS算法在某些情况下也存在偏爱男性的情况。如果男性的排名表完全协调(他们全都列出不同女性作为他们的第一选择),那么在GS算法的所有运行中所有男人最终都与他们的第一选择匹配,而与女人的排序表无关。怎么理解呢?假设男单身狗m1
最喜欢女单身狗w1
,m2
最喜欢w2
,…,mn
最喜欢wn
。那么所有男单身狗在选择时都会进入前面说到的【1】这种情况,也就是直接和最喜欢的女性脱单了。这个时候女性就变成没有选择权了,如果这时候女单身狗的排序表刚好跟男单身狗完全冲突的话,也就是说,w1
最不喜欢m1
,w2
最不喜欢m2
,以此类推。那么这种情境下的匹配结果虽然是稳定的,但却也往往也是带着一股不太好的气息,因为男性都得到了最喜爱的女性,而女性却都得到了最不喜爱的男性。
算法实现
经过前面的讨论我们基本清楚了稳定匹配问题和GS算法是怎么一回事了。下面,我用C++简单实现了GS算法的整个过程。为了在O(1)
的时间内判断出女性w
是否更加偏爱mi
或mj
,我将女性对男性的排序表的存储方式小小调整了一下,跟男性对女性排序表的存储方式有所不同。另外,侠侣集合S
的数据结构也采用数组表示以便更简单地在O(1)
的时间内增加或删除侠侣。
GS算法的时间复杂度为O(n^2)
,但是不同代码实现可能会有不同的复杂度。如果判断女性w
是否更加偏爱mi
或mj
这个地方使用遍历女性w
的排序表的方法的话会造成更高的复杂度。集合的增删元素操作这里也会有相应的复杂度影响。有了上面两个O(1)
复杂度的改进之后,下面整个算法实现的时间复杂度为O(n^2)
。下面是实现代码:
1 |
|
输入输出示例:
1 | 3 |