舞伴问题数据结构java_Gale-Shapley算法解决舞伴问题过程详解(C++实现)
舞伴问题是这样的:有 n 个男孩与 n 个女孩参加舞会,每个男孩和女孩均交给主持一个名单,写上他(她)中意的舞伴名字。无论男孩还是女孩,提交给主持人的名单都是按照偏爱程度排序的,排在前面的都是他们最中意的舞伴。试问主持人在收到名单后,是否可以将他们分成 n 对,使每个人都能和他们中意的舞伴结对跳舞?为了避免舞会上出现不和谐的情况,要求这些舞伴的关系是稳定的。
假如有两对分好的舞伴:(男孩 A,女孩 B)和(男孩 B,女孩 A),但是男孩A更偏爱女孩 A,女孩 A 也更偏爱男孩 A,同样,女孩 B 更偏爱男孩 B,而男孩 B 也更偏爱女爱 B。在这种情况下,这两对舞伴就倾向于分开,然后重新组合,这就是不稳定因素。很显然,这个问题需要的是一个稳定匹配的结果,适合使用Gale-Shapley 算法。
算法实现
首先定义舞伴的数据结构,根据题意,一个舞伴至少要包含两个属性,就是每个人的偏爱舞伴列表和他(她)们当前选择的舞伴。根据 Gale-Shapley 算法的规则,还需要有一个属性表示下一次要向哪个偏爱舞伴提出跳舞要求。当然,这个属性并不是男生和女生同时需要的,当使用“男士优先”策略时,男生需要这个属性,当使用“女士优先”策略时,女生需要这个属性。为了使程序输出更有趣味,需要为每个角色提供一个名字。
综上所述,舞伴的数据结构定义如下:
typedef struct tagPartner {
char *name; //名宇
int next; //下一个邀请对象
int current; //当前舞伴,-1表示还没有舞伴
int pcount; //偏爱列表中舞伴个数
int perfect[UNIT_C0UNT]; //偏爱列表
}PARTNER;
UNIT_COUNT 是男孩或女孩的数量(稳定匹配问题总是假设男孩和女孩的数虽相等),pcount 是偏爱列表中的舞伴个数。根据标准的“稳定婚姻问题”的要求,pcount 的值应该是和 UNIT_COUNT —致的,但是某些情况下(比如一些算法比赛题目的特殊要求)也会要求伙伴们提供的偏爱列表可长可短,因此我们增加了这个属性。
这里给出的实现算法使用bool Gale_Shapley(PARTNER *boys, PARTNER *girls, int count)
{
int bid = FindFreePartner(boys, count);
while(bid >= 0)
{
int gid = boys[bid].perfect[boys[bid].next];
if(girls[gid].current == -1)
{
boys[bid].current = gid;
girls[gid].current = bid;
}
else
{
int bpid = girls[gid].current;
//女孩喜欢bid胜过其当前舞伴bpid
if(GetPerfectPosition(&girls[gid],bpid) > GetPerfectPosition(&girls[gid], bid)) {
boys [bpid].current = -1; //当前舞伴恢复自由身
boys[bid].current = gid; //结交新舞伴
girls[gid].current = bid;
}
}
boys[bid] .next++; //无论是否配对成功,对同一个女孩只邀请一次 bid = FindFreePartner(boys, count);
}
return IsAllPantnerMatch(boys> count);
}
FindFreePartner() 函数负责从男孩列表中找一个还没有舞伴、并且偏好列表中还有没有邀请过的女孩的男孩,返回男孩在列表(数组)中的索引。如果返回值等于 -1,表示没有符合条件的男孩了,于是主循环停止,算法就结束了。
GetPerfectPosition() 函数用于判断女孩喜欢一个舞伴的程度,通过返回舞伴在自己的偏爱列表中的位罝来判断,位罝越靠前,也就是 GetPerfectPosition() 函数的返回值越小,说明女孩越喜欢这个舞伴。
GetPerfectPosition() 函数的实说代码如下:
int GetPerfectPosition(PARTNER *partner, int id)
{
for(int i = 0; i < partner->pCount; i++)
{
if(partner->perfect[i] == id)
{
return i;
}
}
//返回一个非常大的值,意味着根本排不上对
return 0X7FFFFFFF;
}
按照“稳定婚姻问题”的要求,这个函数应该总是能够得到 ID 指定的异性舞伴在 partner 的偏爱列表中的位置,因为每个 partner 的偏爱列表包含所有异性舞伴。但是当题目有特殊需求时,partner 的偏爱列表可能只有部分异性舞伴。比如 partner 非常恨一个人,他们绝对不能成为舞伴,那么 partner 的偏爱列表肯定不会包含这个人。
考虑到算法的通用性,GetPerfectPosition() 函数默认返回一个非常大的数,返回这个数这意味着 ID 指定的异性舞伴在 partner 的偏爱列表中根本没有位罝(非常恨),根据算法的规则,partner 最不喜欢的异性舞伴的位置都比id指定的异性舞伴位置靠前。这也是算法一致性处理的一个技巧,GetPerfectPosition() 函数当然可以设计成返回 -1 表示 ID 指定的异性舞伴不在 partner 的偏爱列表中,但是大家想一想,算法中是不是要对这个返回值做特殊处理?
原来代码中判断位置关系的一行代码处理:
if(GetPerfectPosition(&girls[gid], bpid) > GetPerfectPosition(&girls[gid], bid))
就会变得非常繁琐,让我们看看会是什么情况:
if((GetPerfectPosition(&girls[gid], bpid) == -1)&& (GetPerfectPosition(&girls[gid], bid) == -1))
{
//当前舞伴bpid和bid都不在女孩的喜欢列表中,太糟糕了
...
}
else if(GetPerfectPosition(&girls[gid], bpid) == -1)
{
//当前舞伴bpid不在女孩的喜欢列表中,bid有机会
...
}
else if(GetPerfectPosition(&girls[gid], bid) == -1)
{
//bid不在女孩的喜欢列表中,当前舞伴bPid维持原状
...
}
else if(GetPerfectPosition(&girls[gid], bpid) >GetPerfectPosition(&girls[gid], bid))
{
//女孩喜欢bid胜过其当前舞伴bpid\
...
}
else
{
//女孩喜欢当前舞伴bpid胜过bid
...
}
这是我最不喜欢的代码逻辑,真的,太糟糕了。可见,这个小小的技巧为代码的逻辑处理带来了极大的好处。类似的技巧被广泛应用,在排序算法中经常使用“哨兵”位,避免每次都要判断是否比较完全部元素。面向对象技术中常用的“DuramyObject”技术,也是类似的思想。
Gale-Shapley 算法原来如此简单,你是不是为沙普利能获得诺贝尔奖愤愤不平?其实不然,算法原理的简单并不等于其解决的问题也简单,小算法解决大问题。
改进优化:空间换时间
Gale_Shapley() 函数给出的算法还有点问题,主要是 GetPerfectPosition() 函数的策略,这个函数每次都要遍历 partner 的偏爱舞伴列表才能确定 bid 的位罝,很可能导致理论上O(1) 时间复杂度的方式直接查表确定偏爱舞伴的关系。这样的表可以是for(int w = 0; w < unit_count; w++)
{
//初始化成最大值,原理同上
for(int j = 0; j < UNIT_COUNT; j++)
{
priority= 0X7FFFFFFF;
}
//给偏爱舞伴指定位置关系
int pos = 0;
for(int m = 0; m < girls[w].pCount; m++)
{
priority[w][girls[w].perfect[m]] = pos++;
}
}
最后,将对 GetPerfectPosition() 函数的调用替换成查表:
if(priority[gid][bpid] > priority[gid][bid])
对于一些在算法执行过程中不会发生变化的静态数据,如果算法执行过程中需要反复读取这些数据,并且读取操作存在一定时间开销的场合,比较适合使用这种“以空间换时间”的策略。用合理的方式组织这些数据,使得数据能够在 O(1) 时间复杂度内实现是这种策略的关键。
对本问题应用“以空间换时间”的策略,需要在算法开始的准备阶段初始化好 priority 二维表,这需要一些额外的开销,但是相对于 n2 次查询节省的时间来说,这点开销是能够容忍的。
舞伴问题数据结构java_Gale-Shapley算法解决舞伴问题过程详解(C++实现)相关推荐
- 【数据结构入门】算法的时间复杂度和空间复杂度详解
文章目录 (1)算法效率 (2)时间复杂度的计算 1)什么是时间复杂度 2)大O渐进表示法(估算) 3)时间复杂度计算实例 4)总结 5)一些思考 (3)空间复杂度的计算 (4)常见复杂度对比 本篇前 ...
- 数据结构:弗洛伊德算法(最短路径)图文详解
弗洛伊德算法选取某个节点k作为i到j需要经过的中间节点,通过比较d(i,k)+d(k,j)和现有d(i,j)的大小,将较小值更新为路径长度,对k节点的选取进行遍历,以得到在经过所有节点时i到j的最短路 ...
- Bellman-Ford算法图解及手算过程详解 —— C++代码实现
0. <算法导论>讲解 1. 图24-4 手算过程 2. 代码实现(自己根据算法导论伪代码实现的代码,有错请指出,谢谢) #include<iostream> #include ...
- c语言将AOE网络的数据写入TXT文档中,数据结构与算法学习辅导及习题详解.张乃孝版-C/C++文档类资源...
数据结构与算法学习辅导及习题详解.张乃孝版.04年10月 经过几年的努力,我深深体会到,编写这种辅导书要比编写一本湝通教材困难得多. 但愿我的上述理想,在本书中能够得以体现. 本书的组织 本书继承了& ...
- 【数据结构与算法】哈希算法的原理和应用详解!
在程序员的实际开发中,哈希算法常常能用得到,本文以哈希算法的原理和应用为核心,和大家详细讲解一下哈希算法的概念.常见算法以及原理.在信息安全的应用等等. 一.概念 哈希表就是一种以 键-值(key-i ...
- python模拟手写笔迹_Python实现基于KNN算法的笔迹识别功能详解
本文实例讲述了Python实现基于KNN算法的笔迹识别功能.分享给大家供大家参考,具体如下: 需要用到: Numpy库 Pandas库 手写识别数据 点击此处本站下载. 数据说明: 数据共有785列, ...
- python实验原理_Python实现蒙特卡洛算法小实验过程详解
蒙特卡洛算法思想 蒙特卡洛(Monte Carlo)法是一类随机算法的统称,提出者是大名鼎鼎的数学家冯·诺伊曼,他在20世纪40年代中期用驰名世界的赌城-摩纳哥的蒙特卡洛来命名这种方法. 通俗的解释一 ...
- 分治算法小结(附例题详解)
分治算法小结(附例题详解) 我的理解: 分治算法我的理解就是看人下菜碟,我们要解决的问题就好像一群人构成的集体,要我们解决这个问题,那我们就要满足这群人里面每个人不同的需求,也就是写出解决的代码,把每 ...
- 【论文深度研读报告】MuZero算法过程详解
深度强化学习实验室 官网:http://www.neurondance.com/ 论坛:http://deeprl.neurondance.com/ 作者:饼干Japson(DeepRL-Lab研究者 ...
- 《算法导论》红黑树详解(一):概念
在学习红黑树之前,读者应先掌握二叉查找树的相关知识.学习红黑树或者二叉查找树,推荐大家看<算法导论>.<算法导论>原书第3版 高清PDF 带详细书签目录下载 密码:acis & ...
最新文章
- 读书笔记:《图解HTTP》第三章 HTTP报文
- CSP浏览器安全策略备忘
- Entity Framework 出现 此 ObjectContext 实例已释放,不可再用于需要连接的操作 的错误...
- mysql binlog恢复sql_binlog2sql实现MySQL误操作的恢复
- 彻底解决_OBJC_CLASS_$_某文件名“, referenced from:问题
- SQL语句求解同一人物不同日期,某一属性的差值
- Linux 基础I/O :文件描述符,重定向,文件系统,软链接和硬链接,动态库和静态库
- Failed to parse PID from file /run/nginx.pid: Invalid argument
- com.android.phone lg g3,详细的lg g3 root教程与方法
- java ognl表达式_OGNL表达式介绍
- html设置label的字体大小,swift - label 的font 设置 文字字体和大小
- 互联网架构的演进方向
- 站点(e.g. Hexo Blog)提交百度搜索引擎收录实现SEO
- 从董明珠雷军世纪之赌中看到什么样的格力和小米?
- 深圳大学大学计算机考试科目,深圳大学计算机考研科目有哪些
- Docker-入门基础知识(1)
- 爬取电影网站链接并进入网盘通过验证码下载的python(未完成)
- 配电室环境远程监控物联网方案
- tl494组成的超声波发射电路_最简单无线发射电路图大全(超声波发射/射频收发电路/调频发射器) - 全文...
- Java中快速掌握正则表达式
热门文章
- 奇瑞s61鸿蒙,数码提前曝光,奇瑞新能源 S61 将搭载华为鸿蒙车机系统
- r语言 图形一览_R语言之图形概览
- oracle 查询空值异常,Oracle中的NULL
- 三圆相交阴影部分面积_这题要证明圆的切线并求阴影面积,分割图形求面积法是解题关键...
- C语言面向过程与C++面向对象
- 便携式不锈钢管道焊接机器人_304不锈钢管居然可以发黑!?
- 4.Product-based Neural Networks for User Response Prediction论文详细解读和代码实现
- 相机模型--A Unifying Theory for Central Panoramic Systems and Practical Implications
- 卫星图像分割--Effective Use of Dilated Convolutions for Segmenting Small Object Instances
- GNT格式转换为PNG格式