匈牙利算法

重要说明:本文是个人学习所作,资料绝大部分来源于网络,侵删!

参考博客:

1.匈牙利算法的图论介绍和图论中的应用

2.匈牙利算法的介绍和实际应用

3.匈牙利算法的一个官方例子

目录

一、思考一下

二、有关知识准备

2.1  二分图

2.2  匹配

2.3  最小覆盖

2.4  独立集、交替路和增广路

三、匈牙利算法的图论应用

3.1  最大匹配问题

3.2  最小点覆盖问题

四、匈牙利算法的实际应用

  4.1  一个指派问题的例子

  4.2  匈牙利算法的流程

  4.3  匈牙利算法用于指派问题 

  4.4  程序实现(python


一、思考一下

  问题一:匈牙利算法干什么的?

  问题二:匈牙利算法的原理是什么?

二、有关知识准备

  对于图论而言,匈牙利算法(Hungarian algorithm)主要用于解决图论中二分图的最大匹配问题,因此涉及到二分图、匹配等有关概念。下面对这些概念做一下简单介绍。

2.1  二分图

二分图(bipartite graph):一类特殊的图,它的点集可分成两个子集,任意一个边的端点各在一个子集。

一个二分图例子:

b720a0b711d247218f39b139b597919d.png

2.2  匹配

  匹配:一组边的集合,边有要求 :不能有公共端点

  一个匹配例子:

d2db6a52484247fd9afba6ad1a124aec.png

  引申问题:有多少匹配?最大匹配的边数?

  对匹配进一步观察,可以定义如下的特殊性的匹配: 

  完备匹配:一种特殊匹配,覆盖了原图的所有点。

  二分图与完备匹配:用映射观点看,不能一对多、多对一,只能双向一对一+满射,即双射。有

eq?n%21个完备匹配 。

  完美匹配和最大匹配:完美匹配一定是最大匹配(若完美匹配不是最大匹配,那么还存在更大的,即还能添加新的边,但完美匹配的图中所有点都匹配了),但最大匹配不一定是完美匹配(因为有的图不一定存在完美匹配)。

  最优匹配(最大权匹配) :(若边带有权重)最优匹配是指边权值和最大的那个完备匹配。

  最优匹配与二分图:若X、Y相等,最优匹配必定是完备匹配;若不等,可通过加点补权为0转化。

2.3  最小覆盖

  二分图的最小覆盖分为最小点覆盖和最小路径覆盖,分别解释如下:

  最小点覆盖:最少的点使得每个边至少都被关联了一次;二分图最小定点覆盖数=二分图最大匹配边数。

  最小路径覆盖:最少的路径覆盖二分图所有顶点;二分图最小路径覆盖数=点数-最大匹配边数。

2.4  独立集、交替路和增广路

  最大独立集:一个点集,该集合中任意两点相连路径在原图中无对应;NP完全问题;二分图最大独立集点数=图点数-最大匹配边数。

  交替路:给定图及其匹配,交替路是指一个路径:从未匹配点出发,交替经过匹配边和为匹配边。

  增广路:一个特殊交替路,其终点为一个未匹配点,且该未匹配点不同于起始的未匹配点。

  经过图论学者们的研究,增广路具有一些性质,可参考博客匈牙利算法详解

三、匈牙利算法的图论应用

   匈牙利算法主要解决两个问题:最大匹配的边数与最小路径覆盖数。

3.1  最大匹配问题

  如何用匈牙利算法解决最大匹配问题呢?下面举个形象的例子介绍匈牙利算法:

9d271858aa1b4c148e50c4f9e88c195b.png

  如上面这个二分图,我们这样来看待这个图:假设你是红娘,你最多能撮合多少对情侣?(不可能共用顶点,即最大匹配边数问题) 

  一个可能能行的大致想法:我们还是从遍历所有点的想法开始,只不过可以在遍历过程中设立一些选项使得我们遍历完后得到的边数尽可能大(这里先不考虑是不是理论上最大)

  具体解释下:

    1.从B1开始遍历,随机给他匹配一个G2 

30e955b95d404934aee13e14d20344b0.png

  2.遍历到B2时发现,B2只喜欢G2,不给他匹配不就少了个边?于是返回B1发现B1还有备选,果断给B1换个,这样两全其美。 

1cdd846645924930ba875d5c633cd03e.png

  3.B3随机选,如G1;B4只能选G4,但G4已经和B1匹配了,取消这个匹配又会引发一些连锁反应,于是B4注定单身了。

1ace211dfadf4c6f8d0c89801703147f.png

  程序实现(c++):

int M, N;            //M, N分别表示左、右侧集合的元素数量
int Map[MAXM][MAXN]; //邻接矩阵存图
int p[MAXN];         //记录当前右侧元素所对应的左侧元素
bool vis[MAXN];      //记录右侧元素是否已被访问过
bool match(int i)
{
    for (int j = 1; j <= N; ++j)
        if (Map[i][j] && !vis[j]) //有边且未访问
        {
            vis[j] = true;                 //记录状态为访问过
            if (p[j] == 0 || match(p[j])) //如果暂无匹配,或者原来匹配的左侧元素可以找到新的匹配
            {
                p[j] = i;    //当前左侧元素成为当前右侧元素的新匹配
                return true; //返回匹配成功
            }
        }
    return false; //循环结束,仍未找到匹配,返回匹配失败
}
int Hungarian()
{
    int cnt = 0;
    for (int i = 1; i <= M; ++i)
    {
        memset(vis, 0, sizeof(vis)); //重置vis数组
        if (match(i))
            cnt++;
    }
    return cnt;
}

3.2  最小点覆盖问题

解决这个问题,只需要一个定理:一个二分图中的最大匹配数等于这个图中的最小点覆盖数。

四、匈牙利算法的实际应用

  匈牙利算法(Hungarian algorithm),由匈牙利数学家Edmonds于1965年提出,它是全局最近邻(Global Nearest Neighbor,GNN)数据关联思想的一种具体实现,其最早用于求解经济学领域中的任务分配问题,后来发展成为图论领域中求解二分图有关问题的算法。

  上面已经介绍了匈牙利算法在图论中的应用,下面回归到匈牙利算法最开始提出的时候,探讨一下匈牙利算法在实际中的应用。

  4.1  一个指派问题的例子

   假设有三位工人A,B和C,需要分配他们每人完成一件工作;对于不同的工作他们所需要花费的时间不同,如下表所示。问题就是要找到一套耗时最小的指派方案。用矩阵表示如下:

6aed29b18e4f43b0b93367440300b7ea.png

  4.2  匈牙利算法的流程

7540b7c996c645e38ec16f595b25fdf7.png

  4.3  匈牙利算法用于指派问题 

  第一步:找出每一行中值最小的元素,然后把该行所有元素都减去这一最小值

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYXRocnVuc3Vubnk=,size_18,color_FFFFFF,t_70,g_se,x_16

   第二步:对于每一列,找到该列的最小值,然后该列的数都减去这个最小值

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYXRocnVuc3Vubnk=,size_20,color_FFFFFF,t_70,g_se,x_16

  第三步:用最少的水平线和垂直线覆盖掉矩阵的所有0元素。如果需要n条线,那么在这些0中就存在最优解。算法结束

f634c8680ff143008e9b4e1504685806.png

  第四步:找到没被线覆盖的行列中的最小的元素,记作k。所有没被覆盖的元素都减去k,被覆盖两次的元素加上k

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYXRocnVuc3Vubnk=,size_19,color_FFFFFF,t_70,g_se,x_16

   

  最后:刚好用3条线即可覆盖所有的0,算法结束

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYXRocnVuc3Vubnk=,size_10,color_FFFFFF,t_70,g_se,x_16

  即最后指派A拖地,B擦桌,C扫厕所。

  4.4  程序实现(python)

from scipy.optimize import linear_sum_assignment
 
cost =np.array([[4,1,3],[2,0,5],[3,2,2]])
row_ind,col_ind=linear_sum_assignment(cost)
print(row_ind)#开销矩阵对应的行索引
print(col_ind)#对应行索引的最优指派的列索引
print(cost[row_ind,col_ind])#提取每个行索引的最优指派列索引所在的元素,形成数组
print(cost[row_ind,col_ind].sum())#数组求和

由于时间有限,先做到这里了。更完整内容见参考博客就行啦。

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
匈牙利算法,也称为二分图最大匹配算法,是一种解决二分图最大匹配问题的经典算法。它的目标是在一个二分图中找到最大的匹配数,即找到最大的能够互相配对的节点对数。 Python是一种高级编程语言,具有简洁、易读、易学的特点,广泛应用于各个领域。在Python中,我们可以使用网络流算法库或者自己实现匈牙利算法来解决二分图最大匹配问题。 以下是匈牙利算法的基本思想和步骤: 1. 初始化一个空的匹配集合。 2. 对于每个未匹配的左侧节点,尝试将其与一个未匹配的右侧节点进行匹配。 3. 如果右侧节点未被匹配,或者已被匹配但可以通过其他路径重新匹配,那么将左侧节点与右侧节点进行匹配,并将右侧节点标记为已匹配。 4. 如果右侧节点已被匹配且无法重新匹配,那么尝试将右侧节点的当前匹配节点重新匹配到其他未匹配的左侧节点。 5. 重复步骤3和步骤4,直到无法找到更多的匹配。 在Python中,可以使用networkx库来实现匈牙利算法。以下是一个使用networkx库解决二分图最大匹配问题的示例代码: ```python import networkx as nx # 创建一个空的二分图 G = nx.Graph() # 添加左侧节点 G.add_nodes_from(['A', 'B', 'C']) # 添加右侧节点 G.add_nodes_from([1, 2, 3]) # 添加边 G.add_edges_from([('A', 1), ('A', 2), ('B', 2), ('C', 3)]) # 使用匈牙利算法求解最大匹配 matching = nx.bipartite.maximum_matching(G) # 输出最大匹配结果 print(matching) ``` 以上代码中,我们首先创建了一个空的二分图,并添加了左侧节点和右侧节点。然后,我们使用`nx.bipartite.maximum_matching`函数来求解最大匹配,并将结果存储在`matching`变量中。最后,我们输出最大匹配结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值