Gale-Shapley 稳定匹配算法的C++实现

  • 背景简述
    • 稳定匹配的内涵
    • Gale-Shapley 算法的伪码表示
    • Gale-Shapley 算法的C++实现
      • 实现思路
      • 代码段
      • Gale-Shapley算法的证明
    • 总结

背景简述

Gale-Shapley 算法是针对稳定匹配问题提出的一种经典算法。
背景资料:考虑n对男女的配对,其中每个男生有一个心仪女生排序,而每个女生也有一个心仪男生排序。每个男生、女生都想匹配到自己最心仪的异性,但现实情况中上总会有一个男生(或女生)至少是两个女生的共同的最心仪对象。那么就需要找到一种折中的匹配方式,使得配对后的每对伴侣都不会再因追求更好伴侣,而产生配对破裂,即一种稳定的匹配。

稳定匹配的内涵

即每对配对成功的情侣,总是存在这种情况:
以情侣中的男性 X 举例,假如他的配偶不是他最心仪的异性 A,A 的当前配偶是Y,Y 在 A 心目中排序要比 X 高。那么 A 就不会舍弃当前配偶 Y,而去与 X 进行配对。假设 X 的当前配偶为 B , 同样的,所有在 X 的心目中比 B排名靠前的异性,都是这种情况,那么 B就是 X所能配到的稳定配偶;类似的,B 的稳定配偶也是 X。 则这一对配对就是稳定的。

Gale-Shapley 算法的伪码表示

Initialize each person to be free.

while (some man is free and hasn’t proposed to every woman)
{
Choose such a man m
w = 1st woman on m’s list to whom m has not yet proposed
if (w is free)
assign m and w to be engaged
else if (w prefers m to her fiancé m’)
assign m and w to be engaged, and m’ to be free
else
w rejects m
}

Gale-Shapley 算法的C++实现

实现思路

设置一个男生求婚函数,每次求婚时调用此函数,对其心仪女生进行降序求婚,直至该男生求婚成功;
设置一个女生判断函数,判断此次是否接受该男生的求婚,值得注意一点是,当女生抛弃当前对象而接受目前求婚对象时,函数应将该被抛弃的男生号码以某种形式传出;

设定一个男生待匹配队列 unpaired_man[n];
最开始将所有男生都装入此队列;
每次将队列头部的男生取出,调用求婚函数进行求婚,而被求婚女生则调用判断函数,当发生女生需要抛弃当前对象时,函数应接受到被抛弃男生的号码,将其配偶情况及配对情况进行重置,并压入队列尾部。
循环操作,直至男生待匹配队列为空,则匹配完成。

代码段

#include "stdafx.h"
#include <queue>
#include <iostream>
#include <Windows.h>
using namespace std;class man
{int m[5];int m_ID;bool is_married ;int proposal_count;int married_ID ;friend class woman;friend void GS_Matching(man* _M, woman *_WM);//进行GS算法配对friend void func(man *_m); //输出每个男生的配对信息public:void propose(woman &_wm, man *_M);  //男生的求婚函数,每次对一个女生求婚man(int *_m, int _m_ID)  // 构造函数对男生进行初始化{for(int i=0; i<5; i++){ m[i]=*_m; _m++;}m_ID = _m_ID;is_married = 0;proposal_count = 0;married_ID = 0;}man(){}void printInfo(man _M)//输出每个男生的心仪女生信息{cout<<"这是第"<<_M.m_ID<<"个男生"<<endl;cout<<"他的排序是"<<_M.m[0]<<" "<<_M.m[1]<<" "<<_M.m[2]<<" "<<_M.m[3]<<" "<<_M.m[4]<<" "<<endl;}
};class woman
{int wm[5];int w_ID;bool is_married;int current_manID;int unlucky_m; friend class man;friend void GS_Matching(man* _M, woman *_WM);//进行GS算法配对friend void func(woman *_wm);  //输出女生的配对信息public:woman(int *_wm, int _wm_ID){for(int i=0;i<5;i++) { wm[i]= *_wm; _wm++;}w_ID = _wm_ID;is_married = 0;current_manID=999;  //初始化女生的默认配偶号码;unlucky_m = 999;    //初始化即将被女生抛弃的男生号码;}void printInfo(woman _WM)//输出每个女生的心仪男生信息{cout<<"这是第"<<_WM.w_ID<<"个女生"<<endl;cout<<"她的排序是"<<_WM.wm[0]<<" "<<_WM.wm[1]<<" "<<_WM.wm[2]<<" "<<_WM.wm[3]<<" "<<_WM.wm[4]<<" "<<endl;}void judge(woman &_wm, man &_m, man *_M); //女生对当前求婚男性的判断int is_prefer(man _m, man _current);    //女生判断当前求婚对象是否优于目前配偶
};void man::propose(woman &_wm, man* _M)   //男生的求婚函数,每次对一个女生求婚
{_wm.judge(_wm,(*this),_M);(*this).proposal_count++;
}void woman::judge(woman &_wm,man &_m, man *_M)  //女生对当前求婚男性的判断
{if(_wm.is_married == 0) {_wm.is_married = 1;                          //cout<<_wm.is_married<<"***"<<endl;   //_m.is_married = 1;cout<<"patched"<<endl;   //将男女的结婚状态置为1_wm.current_manID = _m.m_ID;                                // cout<<_m.m_ID<<endl;                     //将女生的当前配偶号码置为男生号码;_m.married_ID = _wm.w_ID;                                  //cout<<_m.married_ID<<endl;                    //将男生的配偶号码置为女生;_m.is_married = 1;} else if(is_prefer(_m,_M[_wm.current_manID-1]))   //女生的当前配偶优于此次求婚男生  {cout<<"please find next"<<endl;    //女生的当前配偶优于此次求婚男生}else {cout<<"Let's be together"<<endl;_m.is_married =1;                                    //将男生的结婚状态置为1_m.married_ID =_wm.w_ID;                         //将男生的配偶号码置为女生;_wm.unlucky_m = _wm.current_manID;              //将待甩掉的号码置为当前配偶号码  //cout<<"当前拟甩掉的配偶号码是:"<<_wm.unlucky_m<<endl;_M[_wm.current_manID-1].is_married =0;           //将女生的当前配偶的结婚状态置为0;_M[_wm.current_manID-1].married_ID =0;           //将女生的当前配偶的配偶号码置为0;_wm.current_manID = _m.m_ID;                     //将女生的配偶号码置为此次求婚的男生号码;  //cout<<"新的伴侣号码是:"<<_wm.current_manID<<endl;}
}int woman::is_prefer(man _m, man _current)      //女生判断当前求婚对象是否优于目前配偶
{int i=0,j=0;while((*this).wm[i] != _m.m_ID) ++i;while((*this).wm[j] != _current.m_ID) ++j;  //匹配当前两个男生 在女生心中的排序情况if (i<j)   {cout<<"prefer yes"<<endl;return 0;}  //女生更喜欢当前求婚者else       {cout<<"prefer no"<<endl;return 1;}  //女生更喜欢目前配偶
}void GS_Matching(man* _M, woman * _WM)            //进行GS算法配对
{queue<man> unpaired_man;                     //构造未配对男生队列for (int i=0;i<5;i++){unpaired_man.push(*(_M+i));}                                                        //将男生都装入未配对男生队列while (!unpaired_man.empty()){cout<<"有"<<unpaired_man.size()<<"个男生等待配对"<<endl;man &temp_m= unpaired_man.front();cout<<"当前求婚的是第"<<temp_m.m_ID<<"个男生"<<endl;unpaired_man.pop();while(temp_m.is_married ==0)                            //循环条件,当目前男生未配对成功时,则进行配对{cout<<"他求了"<<temp_m.proposal_count<<"次婚"<<endl;cout<<"他的当前求婚对象是第"<<_WM[temp_m.m[temp_m.proposal_count]-1].w_ID<<"个女生"<<endl;if(_WM[temp_m.m[temp_m.proposal_count]-1].is_married == 0)   //此时他求婚的这个女生尚未配对{temp_m.propose(_WM[temp_m.m[temp_m.proposal_count]-1],_M);cout<<"他的对象是第"<<temp_m.married_ID<<"个女生"<<endl;/*cout<<"他的求婚成功状态:"<<temp_m.is_married<<endl;cout<<"他的对象的婚姻状态"<<_WM[temp_m.married_ID-1].is_married<<endl;//*/_M[temp_m.m_ID-1] = temp_m;      //同步男生数组中的对象和参与求婚的临时对象cout<<"//---------------------//"<<endl;}else                           //此时他求婚的这个女生尚未配对{cout<<"他要准备挖墙脚了"<<endl;temp_m.propose(_WM[temp_m.m[temp_m.proposal_count]-1],_M);/*cout<<"他自己的号码是:"<<_WM[temp_m.married_ID-1].current_manID<<endl;cout<<"他的求婚成功状态:"<<temp_m.is_married<<endl;cout<<"他的对象的婚姻状态"<<_WM[temp_m.married_ID-1].is_married<<endl;*/_M[temp_m.m_ID-1] = temp_m;  //同步男生数组中的对象和参与求婚的临时对象if (temp_m.married_ID!=0)   //此次他成功求婚{cout<<"他这次挖成功了"<<endl;cout<<"他的对象是第"<<temp_m.married_ID<<"个女生"<<endl;cout<<"这次被甩的男生号码是:"<<_WM[temp_m.married_ID-1].unlucky_m<<endl;int tmp ;tmp = _WM[temp_m.married_ID-1].unlucky_m-1; //取得这个被甩的男生在男生数组中的序号unpaired_man.push(*(_M+tmp));              //将被甩男生压入队列cout<<"待配对男生队列长度+1"<<endl;cout<<"//---------------------//"<<endl;}else   //此次他求婚失败{cout<<"他这次挖失败了"<<endl;cout<<"//---------------------//"<<endl;cout<<"当前求婚的是第"<<temp_m.m_ID<<"个男生"<<endl;}       }//一次挖墙脚行动结束}//(temp_m.is_married ==0),此时队头男生配对成功}//while(!unpaired_man.empty),此时单身男生队列为空
}void func(man *_m)  //输出每个男生的配偶
{for(int i=0;i<5;i++)cout<<"第"<<_m[i].m_ID<<"个男生配对第"<<_m[i].married_ID<<"个女生"<<endl;
}void func(woman *_wm)//输出每个女生的配偶
{for(int i=0;i<5;i++)cout<<"第"<<_wm[i].w_ID<<"个女生配对第"<<_wm[i].current_manID<<"个男生"<<endl;
}int _tmain(int argc, _TCHAR* argv[])
{int a1[5]={2,1,4,5,3};int a2[5]={4,2,1,3,5};int a3[5]={2,5,3,4,1};int a4[5]={1,4,3,2,5};int a5[5]={2,4,1,5,3};int b1[5]={5,1,2,4,3};int b2[5]={3,2,4,1,5};int b3[5]={2,3,4,5,1};int b4[5]={1,5,4,3,2};int b5[5]={4,2,5,3,1};man M[5]={man(a1,1),man(a2,2),man(a3,3),man(a4,4),man(a5,5)};woman WM[5]={woman(b1,1),woman(b2,2),woman(b3,3),woman(b4,4),woman(b5,5)};GS_Matching (M,WM);func(M);cout<<"//----------//"<<endl;func(WM);cout<<"ok"<<endl;system("pause");return 0;}

Gale-Shapley算法的证明

1.算法步数有限的证明:
1)由于男生总是按照降序对女生进行求婚,且男生不吃“回头草”;
2)女生一旦匹配成功后,将采取观望的态度,即不会在没有新的配偶的情况下,选择单身;
那么最大的匹配情况就是,所有男生对所有女生都进行了一次求婚,此时算法终止。

2.算法完备性(即所有人都会配对成功)的证明:
反证法:
1).假定在算法结束后,男生X没有配对成功,那么同样存在一个女生A未配对成功;
2).由于算法结束的最恶劣条件是男生对所有女生都求过一次婚,那么X即对A求过婚,而最终X是单身,即A或早或迟都拒绝(或抛弃)了X;
3).A拒绝X的条件是:A当前有更好的配偶。而依据女生的观望策略,A最终一定会有比X更好的配偶,那么与A单身矛盾,故假设不成立,即算法终止时,每个人都得到了配对。

3.算法稳定性的证明:
反证法:
1).假定算法终止时,A(女)-X(男)是一对不稳定的对(即相互吸引的两个人);
2).情况一:X没有向A求过婚,那么X的当前配偶比A“优秀”(由于X按照降序方式进行求婚),即X更喜欢其当前配偶,那么 A-X是稳定的对;
情况二:X向A求过婚,那么A或早或迟拒绝了X。那么A的当前配偶比X“优秀”,即A更喜欢其当前配偶,那么A-X是 稳定的对;

3).由2)可知,A-X总是稳定的对,假设不成立,即证明算法的稳定性。

4.算法偏向性(男性最佳策略)的证明
反证法:
1).假设GS算法得到的匹配不是所有男性得到最佳稳定伴侣,那么至少有一个男生被其最佳稳定伴侣所拒绝。
2).不妨令第一个被其最佳稳定伴侣拒绝的男生为Y,设其最佳稳定伴侣为A。
那么在非GS算法中,会达到一个稳定匹配 Y-A,设这个稳定匹配算法为S。
3).对Y执行GS算法时,由于Y按照降序进行求偶,且A是Y的最佳稳定伴侣,则在Y的优先序列中,排名在A之前的女生最终都会拒绝Y(略去不表)。
而A或早或迟也会抛弃Y,不妨设 A 抛弃 Y后,选择了Z,A一定更喜欢Z(相对Y)。
4).不妨令在S算法中,Z的稳定匹配为B。
那么可知,Z一定更喜欢B,否则在S匹配算法中,Z与A会进行结合。与A-Y、Z-B稳定匹配相矛盾。
再对Z执行GS算法,由于Z按照降序求偶,那么Z会先向B求婚,而后续A又能与Z匹配成功((3)中得知),那么说明B拒绝了Z。与X是第一个被拒绝的相矛盾。
5).故假设不成立,即GS算法得到的匹配是所有男生均得到最佳稳定伴侣的算法。

总结

  1. GS稳定匹配算法是一个很经典的算法,蕴含着博弈的思想在里边,在很多方面都有应用。其给我的一个深刻的印象就是:主动策略(男生)总是能获得稳定博弈局面下的最优结果,反之则获得最劣结果。
  2. 这是我第一次在CSDN上发表类似的技术文档,仅供个人记录和技术交流,如果有不对的地方,请各位不吝指出。

Gale-Shapley 稳定匹配算法的C++实现相关推荐

  1. 算法 | 盖尔-沙普利(Gale-Shapley)婚姻稳定匹配算法

    盖尔-沙普利[Gale-Shapley]婚姻稳定匹配算法 1 背景说明 2 原理及思路 2.1 问题的描述 2.2 盖尔-沙普利算法的思路 3 程序实现 4 结果分析 5 后记 概要: 本文将要介绍的 ...

  2. 稳定匹配算法python实现

    所谓稳定匹配算法,就是如果男人想出轨,美女女对他没有一点点性趣,并且爱他的女人比老婆丑十万八千里 反之女人亦然.     用数学语言来讲就是,博弈的各方达到纳什均衡点.     '吕布','刘备',' ...

  3. G-S稳定匹配算法详解

    G-S稳定匹配算法详解 GS算法是解决稳定匹配问题(stable matching)的一个优秀的算法. 下面以男女配对的例子来介绍稳定匹配问题并阐述GS算法的具体步骤. GS算法,全称Gale-Sha ...

  4. 【Gale Shapley 婚姻稳定匹配算法实现】

    原理: 所有男性按照好感的高低向对应女性求婚 每个女性在所有的向她发出求婚的男性和其丈夫(如果暂无丈夫则不做比较)选择一个最喜欢的,如果这个最喜欢的是当前的丈夫,则婚姻关系不变,否则与当前丈夫离婚,并 ...

  5. 稳定婚姻问题:Gale–Shapley算法

    (一)问题的引出 在组合数学.经济学.计算机科学中,稳定婚姻问题(英语:stable marriage problem,简称SMP)又称为稳定配对问题(stable matching problem) ...

  6. 婚姻匹配问题---盖尔-沙普利(Gale-Shapley)稳定匹配算法

    问题导入 一共有N位男士和N位女士,每个人都要选择结婚对象.如果有两对夫妻M1F2,M2F1.M1心目中更喜欢F1,但是他和F2结婚了,M2心目中更喜欢F2,但是命运却让他和F1结婚了,显然这样的婚姻 ...

  7. 匹配算法——相亲男女匹配

    时间:20210928 背景:有个相亲活动,需要暗地里给男女进行匹配,毕竟明面上直接说不喜欢哪个异性总是尴尬的.匹配的话,方法众多,并不能让每个人都满意,根据各自的意向,总能计算整体意向都不错的. 太 ...

  8. 男女稳定匹配问题 stable matching

    离散数学课(CSCI 2110)上,讲到一个有趣的问题. 假设有五个男生,五个女生,每个人都在自己心中对五个异性有一定的preference排序,比如: 以上的排序表解读为:男生1最中意女生C,次中意 ...

  9. 软件工程实践2017 结队项目——第二次作业

    作业链接 031502333 GITHUB LINK 前言 近期比较倾向于使用Go语言写小工具,所以在看到作业不限制编程语言时就决定使用Go作为开发语言.而由于Go的诸多优秀特性,使得编写代码效率极高 ...

  10. 开源火种_火种艾完美的牵线搭桥

    开源火种 人工智能,意见,技术(Artificial Intelligence, Opinion, Technology) "Bored in the house, in the house ...

最新文章

  1. 一道Python面试题,据说大部分人都中招了,纷纷开始怀疑自己
  2. java 对象自定义排序_java – 使用自定义排序顺序对对象的ArrayL...
  3. python 12306查询不到车次_python实现自动抢12306火车票,妈妈再也不用担心我没有车回去了...
  4. 2019牛客暑期多校训练营(第十场)C - Gifted Composer (二分+哈希)
  5. Request_共享数据(域对象)
  6. 二:unittest框架配合selenium之xpath定位
  7. 只怪自己当时年纪太小,一心只看那只泼猴了
  8. SpringBoot整合Encahce后,访问页面页面出现异常信息,无法获取返回数据
  9. git diff生成patch用法
  10. vue添加定位功能_vue 实现Web端的定位功能 获取经纬度
  11. STEAM 正在检查可用更新 ,失败
  12. 高中计算机课程网页修改,高中信息技术教学贯彻新课改理念
  13. C# 程序图标设置/winform 图标
  14. 遥感图像场景分类数据集
  15. \t\t【团队精神】荣辱与共 交流分享 精诚协作 尊重理解 ——程序员应知
  16. SpringBoot添加压力测试
  17. Android高仿陌陌应用点点滑动效果
  18. java中一些常用的英语
  19. postgresql 12.0 源码编译安装
  20. 如何建立师资库_HR们如何建立人才库?

热门文章

  1. vue js日期时间格式化
  2. Xshell颜色及PS1
  3. VBA代码行号显示 VBA代码助手独家功能
  4. JavaScript实现视频播放器
  5. java jbutton icon 边框_Swing开发JButton图标效果篇
  6. 图片和文本置顶显示的方法
  7. Microsoft Remote Desktop 10 - 微软官方免费远程桌面控制 Windows 的软件 APP
  8. 电商系统如何搭建会员体系/会员制玩法?
  9. icem合并面网格_icem 混合网格 流沙
  10. c语言傅立叶变换,傅立叶变换与傅立叶反变换的C语言实现