[算法]Gale-Shapley Algorithm-稳定匹配算法的设计、实现与探讨(下)

来源:互联网 发布:字有几个字节知乎 编辑:程序博客网 时间:2024/05/21 22:57

  在上一篇博文我们已经对Gale-Shapley算法进行了设计与实现。也有运行结果验证了婚姻匹配问题。

但是,不知道读者会不会有很多猜想,比如我们在算法设计中,都是男生去选择女生而做出匹配,那么要是女生来选择呢?

猜想:男生优先匹配问题

  在前文我们提到的算法设计中,里面都是对没有配对过的男生进行遍历,来匹配到对应的女生。这样会不会存在一种情况:我们考虑的都是优先遍历选取未配对男生的喜好排序列表,而女生在这个算法上看上去似乎很“被动”?都是由男生先进行与女生匹配,女生再根据自己的喜好排序考虑要不要与之配对。那么问题来了:
倘若我改成女生优先查找喜好的男生进行配对,结果还会一样吗?

  我们可以修改一下Gale-Shapley算法的核心代码,把关于男生和女生的数组进行对调。这是一种方法。

  其实,我们有另外一种更加简便的方法,就是不用修改源码,我们直接修改文本数据,奖男生和女生的喜好排序列表互调,让原本的男生读取到的列表变成为女生的。这样子在程序运行的时候实际上就变成了是女生优先匹配了(虽然打印结果还是显示man对应women,但是这不要紧)

我们把文本数据进行对调:

前===>>>后

  也就是把原先3 5 0 6 9 4 8 7 2 1 这一行到结尾全部都提取剪切到第一行的下面。

  我们再来看看运行截图:
  喜好排序列表
    1

  匹配情况:(对调文本数据后,这里面的men就替换为了women,women就替换为了men了)

    2

  而原先的的匹配结果是:

    M

  我们只要比较原先的Matching for Woman 与现在运行后输出的Matching for Men做对比就行了,发现了还是有些差别,总共有4条配对数据与前面的不一致,比如:
  本来第3个women配对了第0个men,对调了后变成配对了第4个men。

  从结果来看,貌似正是这个算法的弊端,也就是我们在匹配双方的时候,存在一个优先匹配的问题。其实这个问题很好理解,因为算法的思想上,本身就存在着对其中一个对象进行优先选择的问题,比如优先对男生依次从喜好排序列表拿出要匹配的女生,则就可能造成男生的确是匹配到了他最喜欢的女生,而这名女生的最喜欢的男生却不一定就是这个了。而反过来,对女生优先选取,从该女生的喜好排序列表中取出要匹配的男生,同样的数据样本下,这名女生就可能匹配到了她最喜欢的男生,而先前那名男生不一定就没办法匹配到这名女生。

  其实也不能说是弊端吧,毕竟这是自相矛盾的,不可能绝对的存在男生女生相互喜欢程度一样,这放在现实中的场景都是非常正常的:
  A男生追求了A女生,不过A女生最不喜欢的就是A男生,但还是暂时先交个男女朋友吧。

  这时候B男生也来追求A女生,A女生觉得B男生比A男生好,那么就果断与A男生分手,与B男生交往。
如此之下,只要再有男生来跟女生追求的话,那么女生就会考虑是不是更喜欢他而跟他交往。

  但是如果都没有再多的男生来跟这个女生表白的话,那这个女生也没办法啊,毕竟又没有更多的男生来追求,只能和目前的追求者中自己最感兴趣的交往咯。

  再者,延伸到一个高考录取的问题上,在考生的志愿学校和学校的录取学生上。

  考生的第一志愿报到学校来,学校目前人还没录取完,那么就暂时先接受。

  而第二个、第三个考生直到第n个学生也报了这个学校。刚好这个学校现在名额满了,那么学校就要做出按分数录取的选择了,如果这个第n个学生的分数是排在学校的暂时录取的最后一名学生的前面的话,那么就暂时接收这名学生,而把最后一名学生给拒绝接收了,而这名惨被学校拒绝的学生,就只能往第二志愿学校去报名,第二志愿的学校也就按照了同样的做法来做排名录取。该学生最终可能报考到了填的学校,也可能并没有学校要它(当然如果是录取名额等于学生名额的话,那么说什么他都会被录取,只不过可能被更垃圾的学校录取)。

  在这个时候,学校就好比如是女生,只有学生来填报(男生表白),自己才能做出选择。如果实在是没那么多学生来报名,那么分数低的学生,学校也只能接收了。
好了,关于这个优先顺序的问题我们就探讨到这里。

优化:算法的空间和时间复杂度

  在上一篇文章中笔者给出了算法的源码实现,但其实源码是一种“最笨”的实现方式,笔者是根据算法的理论流程而写出来的,并没有经过推敲复杂度的优化,或者是借鉴于其他人的实现方法。所以在这个问题上还是有很多优化问题可以弄的。

  从空间复杂度来说,貌似也没有什么好优化的,就是用到了几个容器来储存必要的信息,并没有其他太多没必要的空间消耗。
  从时间复杂度来说,该算法的时间复杂度从最理想的角度来说,也就是每个男生刚好匹配到最喜欢的女生,那么复杂度则是O(n),只要遍历一趟就OK了,而最坏的情况来说,就是每个匹配过的男生总是后来又被拒绝过,最后匹配总共了最多n次后才成功配对,而在每趟匹配中还有可能遇到女生已经匹配了,需要比较喜好的两个男生的排名,这需要一个O(n/2)的线性时间。那么遍历查找未匹配男生的复杂度是O(n/2),寻找配对女生的复杂度也是O(n/2),完成所有男生的匹配又是O(n)。而相乘起来后就不是线性时间复杂度了,而是在O(n2)之上(不可能是以上复杂度相乘,因为即使最坏的情况下也不存在全部男生都需要匹配最高次数后才匹配成功)。
  当然我们这个问题涉及到一个二维的数据,不可能存在绝对的线性时间复杂度,不过优化一下还是可以的,比如按照我们这样子的实现中,只要一个男生匹配后又被拒绝的话,他还是会从第0个喜欢的女生开始遍历来匹配,而我们知道,这个女生都踢掉你了,那么在这个女生之前那你米更感兴趣的女生肯定不需要再遍历了,因为你们没戏了。再遍历的话不仅造成遍历喜好排序列表的时间,更是在每趟尝试匹配中,可能让女生去做出O(n/2)复杂度的比较。

  所以我们可以设计出一个容器来存放男生的匹配到的女生在喜好排序列表上的索引,这样,当男生被踢出来之后,并不会又开始从0开始匹配女生,而是从那个把男生踢出的女生的后一个来匹配。这样就不会重复把前面自己被踢出来的女生做匹配了。我们的样本数据是10,根据样本的实际情况可能效果并不明显,但是如果样本是10000甚至是100000的话,那么差距就明显多了起来。

  这样子,在匹配女生的时候,就不是O(n/2)了而是在最坏的情况下反而变成了一个常数,他不是每次都是遍历那么多个。

  另外我们在取出未配对男生的时候每次都是从0开始去查找哪个男生没有匹配。其实我们可以提供一个记录被踢出的男生索引的变量k,正常情况下是从0开始遍历第i个未匹配的男生,如果没涉及到替换的话,则k=i,如果遇到女生替换了男生,则记录这个被踢出来的男生的索引到变量k,由于这个男生本来是匹配的了,也就是说肯定是在遍历索引变量i的前面,因为我们可以再下次遍历索引之前,判断如果k是小于i的话,说明k是那个被踢出的男生的索引,那么优先让这个第k个男生去匹配好,然后再来接着下面第i+1个男生进行匹配,倘若又遇到有被替换出来了男生则下一轮又是优先被进行匹配的。也就是说,在执行第i个男生进行匹配之前,肯定是前面的i-1个男生都已经匹配完毕了,不然的话就不会执行第i个了,而是执行第k个。

  由于时间关系,笔者只是进行一个探讨,就不修改源码了。相关更多的复杂度优化肯定存在很多更好的方案,笔者由于能力有限,也无法给出最优复杂度的实现,还希望感兴趣的读者能够给出宝贵的优化意见。

延伸:稳定匹配算法适用于非方阵的数据样本,且每个单位样本都是一个非空集合,即数据不可存在重复。

  在前面我们给出的数据都是n*n的样本数据,其实并不一定非得n*n,就像前面提到的高考填报志愿的问题,学校的数量肯定比学生报考志愿能填报的学校数量要多的多。

  事实上,这样子做可能会造成一些数据并没有找到匹配。

  所以在处理非方阵数据的时候,我们也只能是进行能够匹配到的做出匹配,而摒弃掉哪些匹配不到的,或者是说哪些匹配不到的数据再做另外的一些二次匹配处理。比如学生填报志愿,并不能保证他一定能匹配到学校。不过录取不到学校后,还是有可能通过补录而进去学校的,这就是二次匹配处理。

  上面说每个单位样本都是一个非空集合,意思就是说,比如一个男生,他可以只有1个喜欢的女生或者是n个,但是不可以是0个,这样子就失去了进行匹配的意义了。通常这种情况已经可以把这类数据先遍历过滤掉了。

  后面接着一句“即不可重合”,也就是说既然单位样本一定是一个集合,不会存在重复的数据,比如男生不可能在喜好排序列表里面同时出现两个女生,这样子违背前面的结论:“匹配算法的稳定性”。因为要是存在多个一样的数据的话,就可能出现匹配不稳定,甚至陷入缺少良好的算法设计的死循环中。

  时间问题,关于Gale-Shapley Algorithm-稳定匹配算法我们就探讨到了这里。

阅读全文
0 0