本文共 3342 字,大约阅读时间需要 11 分钟。
今天是算法数据结构专题的第30篇文章,我们来聊聊一个充满趣味性的婚姻匹配问题。这是我们ACM校队当年用来向新生宣讲的例题之一。我们选择这个问题的原因是,新生们通常会对找对象的算法感兴趣,因为这种话题容易吸引他们的注意力。
婚姻匹配,也被称为稳定匹配问题(CP匹配),场景简单而贴近生活。假设有N名男生和N名女生参加相亲活动。每个男生和女生都会对异性心中有一个评价,确定自己最喜欢的对象以及最讨厌的对象。每个人都会给自己排名,形成一个优先级顺序。
我们的目标是设计一个算法,将N名男生和N名女生组成稳定的CP。随便配对虽然简单,但不稳定的情况可能会发生。我们希望每对情侣都能长久地在一起,这样整个婚姻匹配才算成功。
为了更好地理解稳定匹配,我们可以以一个简单的例子来说明。假设有两名男生(男1和男2)和两名女生(女1和女2)。如果我们将男1配对女2,男2配对女1,那么就会出现一个问题:女1和男1之间的喜欢程度比女1和男2更高,而女2和男2的喜欢程度也比女2和男1更高。这种情况下,这对CP是不稳定的,因为最终男1和女1会分手。
为了简化问题,我们假设一定会发生这种情况,最终男1会和女1在一起。我们的目标是设计一个算法,能够将所有男女配对成稳定的CP。
对于这个问题,有多种思路可以实现。一些人可能会想,给每个男生和女生根据对方的欢迎程度打分,然后根据这些分数来安排配对。优质男生和优质女生受到对方的关注较多,因此优先将他们配对。剩下的男女再慢慢处理。但这种方法在实际操作中会遇到很大的问题,因为优质男女之间以及优质男女与非优质男女之间都可能出现不稳定的情况。
另一种方法是使用搜索算法来寻找稳定的配对。由于男女配对的搜索空间明确,我们可以列出所有可能的配对情况,然后逐一筛选,找到最终的稳定解。这种方法虽然可行,但复杂度很高,因为大部分搜索情况都是无效的。
有没有效率更高且能充分解决问题的方法呢?答案是肯定的,并且非常简单。我们可以让每个男生根据自己心中的排名去追求女生。这样会出现多个男生同时或先后追求同一名女生的情况。我们做一个简单的假设:女生始终会选择自己排名中更高的男生作为自己的CP。
具体来说,第一轮让所有男生去追求自己最心仪的女生。经过一系列竞争,会有一些男生成功组成CP。第二轮,让单身的男生再去追求自己第二喜欢的女生。如此循环往复,直到所有人都配对。
这种方法看似简单,但能保证所有男女都能找到对象,也能避免配对不稳定的情况出现。
我们可以通过两个简单的证明来验证这种方法的正确性。
不会出现配对失败的情况:假设存在一名男生和一名女生最后未能配对成功,那么这名男生必须已经向所有女生发起过追求,但都被拒绝了。但根据我们的规则,女生在只有一个追求者时会接受他的追求,这与假设矛盾。因此,配对失败的情况是不可能的。
不会出现配对不稳定的情况:假设存在一对CP不稳定的情况。例如,男1和女1彼此都更喜欢对方,但他们却没有在一起。根据我们的规则,男1会在第一轮就追求女1。如果女1当前的对象不是男1,那么女1会接受男1的追求。即使女1的当前对象是男1,只要男1比女1的当前对象更高排名,女1也会选择男1。因此,这种情况也是不可能的。
Gale-Shapley算法的实现相对简单。我们需要记录男生和女生的匹配情况,以及男生向女生发起追求的轮次。具体实现步骤如下:
以下是我们实现的Gale-Shapley算法的代码示例:
import randomimport sysdef generate_preferences(n): # 生成随机的偏好排序 return [random.randint(0, n-1) for _ in range(n)]if __name__ == "__main__": n = int(sys.argv[1]) boys = [] girls = [] for _ in range(n): boys.append(generate_preferences(n)) girls.append(generate_preferences(n)) girls_matched = [-1] * n boys_round = [0] * n boys_matched = [-1] * n while True: all_matched = True for i in range(n): if boys_matched[i] != -1: continue current_boy = boys[i] girl_index = current_boy[boys_round[i]] girl = girl_index if girls_matched[girl] == -1: girls_matched[girl] = i boys_matched[i] = girl boys_round[i] += 1 else: mate = girls_matched[girl] mate_rank = girls[girl].index(mate) current_rank = girls[girl].index(i) if current_rank < mate_rank: boys_matched[i] = girl girls_matched[girl] = i boys_round[i] += 1 if all_matched: break print("匹配结果(男生):", boys_matched) print("匹配结果(女生):", girls_matched) 运行上述代码可以看到具体的配对结果。例如,在N=4的情况下,第一轮的结果可能是[0, 3], [1, 4], [3, 0], [4, 2]。其中,2号和3号男生都向0号女生发起追求,0号女生选择了3号男生,2号男生未能成功。
第二轮,2号男生向2号女生发起追求,但由于2号女生已经和最佳心仪的4号男配对,2号男生的追求失败。继续向3号女生发起追求,3号女生的当前对象是0号男,由于0号男排名较低,0号男被甩。第二轮的结果是[1, 4], [2, 3], [3, 0], [4, 2]。
最终,所有男女都成功配对,且没有不稳定的情况出现。
Gale-Shapley算法虽然简单,但其背后的逻辑非常深刻。它实际上是二分图匹配问题的经典解决方案。通过这个问题,我们可以看出,主动追求和优先选择是婚姻稳定匹配的关键。在实际生活中,这种算法的思想已经被广泛应用于医学院的毕业生分配工作等场景。
如果你还未找到心仪的对象,不妨试试这种方法,或许能找到属于自己的幸福!今天的文章就到这里,感谢你的关注和支持。
转载地址:http://khqfz.baihongyu.com/