Gale-Shapley算法,匈牙利算法和KM算法三种匹配算法的总结

最近接触的匹配算法有三种:Gale-Shapley算法,匈牙利算法和KM算法 。


Gale-Shapley算法:

是用来求得一个稳定匹配使用的,它又称为 “ 求婚-拒绝算法 ”,可以说这个算法的名字起得也是非常知名达意了。

就拿男人向女人求婚打比方,为了使最后匹配得到的匹配集趋于稳定,就是说当前男人和女人的关系是相对稳定的,不会说存在两对男女,其中一对的男人和另一对的女人之间的好感比他们当前匹配的配偶更高的现象的出现。怎么做到这样的匹配呢?

可能 “ 你喜欢的样子我都有 ” ,存在一种女人让所有男人都对她有很高的好感,也可能“  萝卜白菜各有所爱  ”,这个男人不喜欢的女人可能恰恰有另一个男人喜欢的要命。所以将男人集合中的每个男人都对女人集合中的所有女人有一个独属于自己的排名,女人也是一样。

这样,对于男生集合中的每一个男人,他按照自己的偏好列表依此向女人求婚,一个还没有男朋友的女人面对追求者的求婚就先假意答应吧,以后有更好的再换一个不就是了?这样,后面的男人如果发现当前他求婚的女人有了男朋友,就问问这个女人,我和你现在的男朋友你更喜欢谁?女人要是对这个男人还没有自己男朋友来感的话就让这个可怜的男人去找别的他喜欢的女人吧,要是觉得这个男人比自己男朋友强点就果断让自己男朋友走吧,然后和这个追求者在一起。

想想这个算法也是挺好玩,哈哈哈,哪个男人不花心,不是先追求自己最喜欢的女人?哪个女人见到比自己男朋友好的男人不会多想想?哈哈哈,玩笑话~算法传递出来的感觉是如此。

看到这儿,基本上就理解了求解稳定匹配的算法了吧,事实上GS算法也是求解稳定匹配的一个好帮手,把这个算法用到婚介所会不会有点意思哇!

GS的关键如下:
 

bool Gale_Sharply(PARTNER *girls, PARTNER *boys, 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;
			if (GetPerfectPosition(&girls[gid], bpid) > GetPerfectPosition(&girls[gid], bid)) {
				boys[bpid].current = -1;
				girls[gid].current = bid;
				boys[bid].current = gid;
			}
		}
		boys[bid].next++;
		bid = FindFreePartner(boys, count);
	}
	return IsAllPartnerMatch(boys, count);
}

 


匈牙利算法:

对于一个有2n个节点的二分图,其最多可以有n*n条边,这不难想到。怎么样把二分图中不同集合中的节点按照边匹配起来而使剩下来的独立节点最少呢?匈牙利算法 就是一个求解二部图最大匹配的算法。

?这段话看完图解后看哇!!!

也是假设初始有一个自由男人和自由女人的集合,就是大家都是单身,然后从男人出发去找他喜欢的女人(这个男人和女人之间有连线就说他们相互喜欢),如果这个女人恰巧也是单身,他们俩就先凑活着过呗,就先把他们匹配成一对...过了一会出现了这样一种现象,当一个男人A向他喜欢的女人①表明心意的时候发现,诶,她有男朋友了啊,那么男人A就去问这个女人①的男朋友:男人B,你是不是非她不可了鸭?男人B就说那我看看除了她我还喜欢谁,男人B搜索自己喜欢的女人,发现自己还喜欢女人②,他就去看女人②是不是单身,要是单身了,男人B就把自己原来女朋友①让给男人A,自己选择女人②。要是发现女人②也有男朋友,就重复做和男人A一样的事情...可能到最后发现这条路子行不通,那男人B就开始看看自己还有除了女人①和女人②还有没有别的喜欢的女人,重复上个过程...这样的结果就是或者一些男人和女人通过其他的连线配对了而使最初求婚的男人A最终与女人①在一起了,或者始终无法调剂,男人A和女人①最终是无法在一起的。。。按这样的方式一直匹配直到剩下的单身男女数量最少,我们就说通过匈牙利算法得到了一个最大匹配。值得一提的是,当所有男人和女人都匹配成功后,得到的便是最大匹配的一个特例:完美匹配。

用图表示的话就是:

假设当前情况是这样的,男人B和女人①是一对,男人C和女人②是一对,这时到男人A选女朋友了,他只喜欢女人①,但是女人①是有男朋友的,于是男人A问男人B除了①你还喜欢别人吗,B发现自己还喜欢②,就去问②,但是②也是有男朋友的,于是男人B就问女人②的男朋友男人C有没有别的选择,C发现自己还喜欢③,于是问题就迎刃而解了:C把②让出来,自己选择③,B把①让出来自己选择②,最后A成功和①在一起了。原来的两队匹配也成功增加了一对,现在有三对匹配啦!

通过总结我们发现下面这张图中的匹配线,正好是上面匹配线的反,这些有关增广路线的问题就不具体说了。

匈牙利最大匹配算法的代码关键部分是这里:

bool FindAugmentPath(MAX_MATCH *match, int xi) {
	for (int yj = 0; yj < UNIT_COUNT; yj++) {
		if ((match->edge[xi][yj] == 1) && (!match->supposed_on_path[yj])) {
			match->supposed_on_path[yj] = true;
			if ((match->real_on_path[yj] == -1) || (FindAugmentPath(match, match->real_on_path[yj]))) {
				match->real_on_path[yj] = xi;
				return true;
			}
		}
	}
	return false;
}

bool Hungry_Match(MAX_MATCH *match) {
	for (int xi = 0; xi < UNIT_COUNT; xi++) {
		if (FindAugmentPath(match, xi)) {
			match->max_match++;
		}
		Clear_Supposed_On_Path_Sign(match);
	}
	return (match->max_match == UNIT_COUNT);
}

 


 

KM算法:

我们知道图中的线上可能是带有权重的,有时候权重代表花费,有时候代表时延,有时候代表负载率,欸,是不是有点像计算机网络里面网络路由的相关概念呢,计算包从哪路由可以得到更高的响应率,从哪儿有会带来最大的开销,这样就引出了带权图的匹配的最大权最小权问题。

KM是讨论二分图中最大权最小权问题的一种算法,怎么连两个集合中的点,将他们匹配得到的匹配线上的权值之和最大呢,按照贪心的算法,永远找节点之间权值最大的不就好了,这样又带来一个问题:一个节点出现在多个匹配的对中,这是不允许的。那么我们换一种方式:将节点分成左右两部分,给每个点一个特殊的值,左边集合中的每个节点的,这个值等于每个节点出边的最大的权重,右边集合中每个节点的这个值都等于0。设左边节点集合为X,右边节点集合为Y,则将满足这样条件的线和节点提取出来作为一个子图:X[i]+Y[j]==Weight[i][j];然后当匹配出现冲突时用上述的匈牙利增广路线方式避免冲突,只有冲突无可避免时考虑将左边参与冲突的节点的这个值减去一个数x,右边参与冲突的点的这个值加上这个数x。使更多的边加入基本子图构成相等子图再进行调剂。这个值x的选择应该是做出改变后至少有一条线加入进基本子集。哪这样就选在其中的X集合中的节点和不在其中的Y集合中的节点X[i]+Y[j]-Weight的最小值就好了,这就是KM算法的核心~~代码后续更新哇!


希望大家都能理解各种匹配算法哇!!有不对的地方请不吝赐教哒哒哒!

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
稳定匹配问题是一个经典的算法问题,其中Gale-Shapley算法是一种常用的解决方案。下面我简要介绍如何使用C语言编写程序来求解稳定匹配问题。 首先,我们需要定义一个结构体来表示每个人的偏好列表和当前匹配状态。偏好列表可以用整数数组来表示,数组的索引表示其他人的编号,数组的值表示对这个人的偏好等级。例如,假设有n个人,我们可以定义一个结构体如下: ```c typedef struct { int n; // 人数 int **preference; // 偏好列表 int *matching; // 当前匹配 } MatchingProblem; ``` 然后,我们可以使用如下的Gale-Shapley算法来求解稳定匹配问题: 1. 初始化所有人的匹配状态为空。 2. 循环直到没有人再能够改变匹配状态为止: a. 对于每个未匹配的人,选择他偏好列表中的下一个候选人,记为当前候选人。 b. 如果当前候选人未匹配,则直接将其与该人匹配。 c. 如果当前候选人已经匹配,判断当前候选人是否更优于当前匹配。如果是,将当前匹配与当前候选人匹配,将原先与当前候选人匹配的人重新放回未匹配状态。 3. 输出每个人的匹配结果。 在C语言中,我们可以使用指针和动态内存分配来操作偏好列表和当前匹配状态。具体实现可以参考下面的伪代码: ```c void gale_shapley(MatchingProblem *problem) { int *proposals = malloc(sizeof(int) * problem->n); // 存储每个人的偏好索引 int *acceptances = malloc(sizeof(int) * problem->n); // 存储每个人的匹配状态 // 初始化匹配状态和偏好索引 for (int i = 0; i < problem->n; i++) { acceptances[i] = -1; // -1表示未匹配状态 proposals[i] = 0; // 初始化偏好索引为0 } while (1) { // 查找未匹配的人 int unmatched = -1; for (int i = 0; i < problem->n; i++) { if (acceptances[i] == -1) { unmatched = i; break; } } if (unmatched == -1) { // 所有人都已匹配 break; } // 获取当前候选人 int current = problem->preference[unmatched][proposals[unmatched]]; proposals[unmatched]++; if (acceptances[current] == -1) { // 当前候选人未匹配 acceptances[current] = unmatched; acceptances[unmatched] = current; } else if (problem->preference[current][unmatched] < problem->preference[current][acceptances[current]]) { // 当前候选人更优 acceptances[acceptances[current]] = -1; // 原来的匹配放回未匹配状态 acceptances[current] = unmatched; acceptances[unmatched] = current; } } // 输出匹配结果 for (int i = 0; i < problem->n; i++) { printf("Person %d is matched with Person %d\n", i, acceptances[i]); } free(proposals); free(acceptances); } ``` 通过以上的程序,我们可以使用Gale-Shapley算法来求解稳定匹配问题。该算法具有良好的时间复杂度,并且可以保证返回的匹配结果是稳定的。希望这个回答可以帮助你理解如何使用C语言编写程序来求解稳定匹配问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值