匹配算法告诉你为什么要找女(男)朋友一定要主动?

Posted 陆嵩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了匹配算法告诉你为什么要找女(男)朋友一定要主动?相关的知识,希望对你有一定的参考价值。

稳定匹配:该如何给男孩女孩们配对?

纯文字描述稳定匹配(stable matching)。

定义

有这样一个问题,大概意思说是给一群男的和一群女的配对,每个男的对所有女的喜欢程度都有一个排序,所有女的对所有男的喜欢程度也有一个排序,男女人数相等。

我们给一个不稳定对(blocking pair)的定义。假设小明的女朋友更喜欢小东,比起小东自己的女朋友,小东更喜欢小明的女朋友,这时候,(小明的女朋友,小东)就是一个不稳定对,因为他们比起现有的匹配,更喜欢对方,就很容易搞在一起。

一个匹配是稳定匹配,当且仅当这当中不存在任何不稳定对。

事实上,自由的交易不会导致稳定的婚姻。(会出现环)

算法:延迟接受(盖尔沙普利算法)

有一个简单的想法是,对于约会的配对,大家都去追自己最心仪的女生。而这个女生面对几位追求者,要立刻做个决定。被拒绝的男生们调整一下心情,再去追求心中的 No. 2。重复这个过程。

这样做法有一个严重的问题:当你被你的 No.1 拒绝后,再去追求你的 No.2 的时候,你心中的 No.2 可能已经在第一轮中选择了其他人。

但坑爹的是,有可能你正是你心中 No.2 心中的 No.1,但是她并不知道。所以她在第一轮中,因为没有被你追求,而屈就他人。比及你在第一轮中表白失败,再去找你的 No.2 时,已然晚矣。这时,其实就产生了不稳定的匹配。

那么,有什么方法可以产生稳定的匹配吗?

有一种算法,叫延迟接受算法,可以产生稳定的匹配。它的基本过程如下:

  • 第一轮所有的男的都向他们心中的 No.1 示爱。
  • 当收到男孩子的表白后,收到了两个以上的表白需要做出自己的选择,mark 一个自己最喜欢的(mark 不代表最终选择,先 hold 住的意思),而拒绝其他人。
  • 在第二轮中,第一轮被拒绝的男生再向心中的 No.2 示爱。被示爱的女孩子们,还是 hold 住手中最好的 offer,而把其他拒绝掉。
  • ……
  • 不断重复如上过程,直到男孩无爱可示。

一个简单的观测是,在这个一轮一轮迭代的过程当中,女孩子的示爱对象会越来令自己满意,而男孩子示爱的对象会是越来越不满意的。
可以看得出来,这个算法必然会终止的,因为每个每个男性的示爱对象是有限的。
这个算法最后的结果一定是男女一一配对。

延迟接受算法产生了稳定的匹配。

我们用文字简单说明一下这一点。简单反正。假如(小明,小明女朋友)、(小东,小东女朋友)是配对结果,且(小明,小东女朋友)是一个不稳定对。

因为小东女朋友更喜欢小明,却和小东在一起了,说明了,小明没和小东女朋友表白过。然而,小明更喜欢小东女朋友,却和小明女朋友一起了,说明他曾经和小东女朋友表白且被拒绝。小明即和小东女朋友表白过,又没和小东女朋友表白过,矛盾。

越主动配偶越好

定义: 如果存在一个稳定匹配中男性 m m m 和女性 w w w 匹配在一起, 则称女性 w w w 是男性 m m m 的正当配偶。
一个匹配是稳定匹配,并且所有男性分到的配偶都是最佳的正当配偶,那么称这个匹配是男性最优。

事实上,上面的延迟接受算法产生的匹配一定是男性最优的。我们可以用文字来简单说明一下这点。还是用反证法。

假设延迟算法得到的匹配 1 不是男性最优的匹配,那么必然存在一个稳定匹配 2,存在某个男性,这个男性在匹配 2 中的配偶比匹配 1中的好。我们不妨假设,在算法滚动当中,第一个被更好的正当配偶拒绝的男性为 A。它在匹配 1 中的配偶是 a。假定 A 在匹配 2 中的配偶是 b(即匹配 1 算法中,拒绝 A 的就是 b)。假定 b 在拒绝 A 的时候手中的 配偶是 B,B 在匹配 2 中的正当配偶是 c。

  • 算法过程告诉我们,b 更喜欢 B 比起 A。

  • B 也更喜欢 b 比起 c。否则,如果他更喜欢 c 的话,他在被 b hold 住(即 A 选择 b 被拒)之前,必然选择 c 被拒绝过。这个和 A 是第一个被更好的正当配偶拒绝的男性矛盾。

以上两点告诉我们,b 更喜欢 B 比起 A,B 也更喜欢 b 比起 c,那么匹配 2 就不是稳定匹配,这和开始的假设,匹配 2 是个稳定匹配矛盾。

由此推出了,延迟接受算法产生的匹配一定是男性最优的。

类似地,可以证明这个算法中,女性一定分配到的是最差的正当配偶。

代码实现

直接抄一下别人的代码。

class Solution {
public:
    vector<vector<int>> stableMatching(vector<vector<int>> man_list, vector<vector<int>> woman_list) {
        int man_num = man_list.size();
        int woman_num = woman_list.size();
        // 男女数量不同,无法完美匹配
        if (man_num != woman_num) return {};
        int n = man_num;

        // 将女性喜好列表的索引和数据置反,方便处理
        vector<vector<int>> inverse_womanlist;
        for (int i = 0; i < n; ++i) {
            vector<int> prefer(n, -1);
            for (int j = 0; j < n; ++j) {
                prefer[woman_list[i][j]] = j;
            }
            inverse_womanlist.push_back(prefer);
        }
        // 记录最终的匹配结果
        vector<vector<int>> S = {};
        // 记录单身男性
        queue<int> free_man;
        for (int i = 0; i < n; ++i) {
            free_man.push(i);
        }
        // husband[w] = m:女性 w 的伴侣是 m
        vector<int> husband(n, -1);
        // 为每个男性维护一个指针(索引),记录喜爱列表中下一个求婚的女性索引
        vector<int> man_pointer(n, 0);
        // G-S算法主体
        while (!free_man.empty()) {
            int m = free_man.front();
            int w = man_list[m][man_pointer[m]];
            // 男方求婚指针向前
            man_pointer[m]++;
            // 女方无配偶
            if (husband[w] == -1) {
                S.push_back({m,w});
                free_man.pop();
                husband[w] = m;
            }
            // 男人m比当前配偶更有吸引力
            else if (inverse_womanlist[w][husband[w]] > inverse_womanlist[w][m]) {
                // 找到 S 中原来关于 w 的一对
                int index;
                for (index = 0; index < S.size(); index++) {
                    if (S[index][1] == w) break;
                }
                int old_m = S[index][0];

                // 从 S 中删除 m'-w
                S.erase(S.begin() + index);
                // 在 S 中添加 m -w
                S.push_back({m, w});
                free_man.pop();
                // w 原来的配偶变为单身汉
                free_man.push(old_m);
                husband[w] = m;
            }
            else {
                // w 拒绝 m
            }
        }
        return S;
    }
};

int main() {
    // 索引代表男性编号(0,1,2),数组代表喜好女性的列表
    vector<vector<int>> manlist = {{0,1,2},
                                   {1,0,2},
                                   {0,1,2}};
    // 索引代表女性编号(0,1,2),数组代表喜好男性的列表
    vector<vector<int>> womanlist = {{1,2,0},
                                     {0,1,2},
                                     {0,1,2}};
    Solution test;
    vector<vector<int>> result;

    result = test.stableMatching(manlist, womanlist);
    for (int i = 0; i < manlist.size(); ++i) {
        cout << result[i][0] << " - " << result[i][1] << endl;
    }
    return 0;
}

对于恋爱的启示

这个稳定匹配的算法,本质上就是自由的男同胞们不断地从高到底向女性们求爱,女性们总是在手里保留一个最佳 offer。在这个过程中,男的不断地被拒绝,而女的似乎掌控着更多的自主权,总是挑选一个最好的。

因为男的是被选择人,女的是选择人,从这个层面上看,似乎这个算法是对女方是更有利的。然而,出乎意料的是。最后的匹配的结果确是,在稳定匹配中,男生们选到了最佳选择,而女生们选到了最差的选择。

由此可见,找对象嘛,主动比被动最后得到的结果会更好。这也解释了为什么主动的“渣男”们总能找到高质量的女生的原因,而想我这样的老实人,注孤生。

既然主动表达爱意的一方总是占据上风,So,Boys and Girls,主动一点吧。

以上是关于匹配算法告诉你为什么要找女(男)朋友一定要主动?的主要内容,如果未能解决你的问题,请参考以下文章

单例模式,谁说程序猿没有女(男)朋友?

清华博士教你如何用推荐算法技术「找到女朋友」

清华博士教你如何用 Python 推荐算法「找到女朋友」

余生,一定要找一个随时能和你聊天的人

年轻人,你为什么来阿里做技术?

女生一定要看,男生看完转告你的女性朋友