分配问题与Hungarian算法
分配问题与Hungarian算法
匈牙利方法是一种能够在多项式时间内解决分配问题(assignment problem)的组合优化算法。它由Harold Kuhn 与1955年发展并提出,由于该算法很大程度上依赖于先前两位匈牙利数学家:Denes Konig 和 Jeno Egervary,所以被命名为“匈牙利方法”。
1957年James Munkres重新审视了这个方法,证明发现该方法是严格polynomial的,所以之后该方法也被称为Kuhn-Munkres 算法或者Munkres分配算法。原始的匈牙利算法的时间复杂度是,然而之后Edmonds和Karp,以及Tomizawa独立发现经过一定的修改,该算法能改达到
的时间复杂度。 Ford和Fulkerson将该方法扩展到一般运输问题的求解上。 2006年,研究发现Carl Custav Jacobi在19实际就解决了assignment问题,并且在其逝世后的1890年求解过程被以拉丁语形式发表。。。
指派问题
匈牙利法解决的指派问题应该具有两个约束条件
- workes 和tasks的数目应该相同,即o2o问题。 
- 求解的是最小化问题,如工作时间的最小化、费用的最小化等等 
指派问题示例:
有三个workers: Jim, Steve和Alan,现在有3个工作:clean the bathroom, sweep the floors和wash the windows需要交给这三个人,每个人只能完成一个任务,对应的cost matrix如下
| --- | Clean bathroom | Sweep floors | Wash windows | 
|---|---|---|---|
| Jim | $2 | $3 | $3 | 
| Steve | $3 | $2 | $3 | 
| Alan | $3 | $3 | $2 | 
那么如何分配任务是开销最小就是一个指派问题
匈牙利法步骤
问题: 假定某单位有甲、乙、丙、丁、戊五个员工,现需要完成A、B、C、D、E五项任务,每个员工完成某项任务的时间如下图所示,应该如何分配任务,才能保证完成任务所需要的时间开销最小?

解:
- 写出系数矩阵  
- 更新系数矩阵,使系数矩阵的每一行每一列都减去该行该列的最小值,保证每一行每一列都有0元素出现,参见定理2. 
- 选择只有一个0元素的行或列将该0元素标注为独立0元素,并将该0元素所在的列或行中0元素划掉,直至找不到满足条件的行或列,需要注意的是在循环时,划掉的0元素不再视为0元素。比如我们找下面这个系数矩阵的标注0元素和划掉的0元素 

那么我们用1表示0元素,0表示非0元素,用2标注独立0元素,-2表示划掉0元素,依次得到的中间结果为

可以发现我们在标注第一行第2个0元素时,并没有把已经划掉的第一个0当作0元素看待。
- 划盖0线。 
 a. 首先找到不含有独立0元素的行,将行标注 (4)
 b. 找到标注行中划掉的0元素所在的列,将列标注 (2,3)
 c. 将标注列中独立0元素所在的行标注 (4, 1, 2)
 d. 重复b,c步骤知道所有的标注行中不再存在划掉的0元素
 (行:4 ,1, 2, 3 ; 列: 2, 3, 1)
 e. 在所有标注的列上做盖0线,在所有未被标注的行上做盖0线,我们在矩阵中用3表示被盖0线覆盖,则 - 可以发现所有的0元素都被覆盖掉,所以称为盖0线。 
 根据定理1,如果此时盖0线的个数等于矩阵的维数,相当于找到n个独立0元素,则跳到步骤7,否则步骤5更新系数矩阵。
- 更新系数矩阵。 
 a. 找到未被盖0线覆盖的元素中的最小值
 b. 所有未被盖0线覆盖的元素减去最小值
 c. 所有盖0线交叉处的元素值加上最小值
- 重复步骤4,5 
- 计算最优解。 
 类似步骤3中找独立0元素的方法在C中找到n个不同行不同列的0元素所对应的位置。
定理1. 系数矩阵C中独立零元素的最多个数等于能覆盖所有零元素的最少线数。 —— D. Konig
定理2. 若将分配问题的系数矩阵每一行及每一列分别减去各行及各列的最小元素,则新的分配问题与原分配问题有相同的最优解,只是最优值差一个常数。
当然,注意找盖〇线的方法并不是唯一的,比如下述方法
- 同上一方法 
- 同上一方法 
- 在C中寻找未被盖〇线覆盖的存在0元素且数组最少的行(列),标注一个0元素作为独立0元素,该0元素所在的列(行)做盖0线。重复此步骤,直至找不到符合条件的行或列,已经被找过的行(列)就不再找了! 
- 若是盖0线数组等于矩阵维度,则跳到7 
- 同上一方法 
- 重复3,4,5 
- 同上一方法 
当然还有别的方法,比如从包含最多0元素的行或列开始做盖0线直到将所有的0元素覆盖掉等
这里代码实现了上面两个方法,注释掉的是第二个方法的核心
- function [cost,CMatrix]=Assignment(C,ismin) 
 
- % Assignment problem solved by hungarian method. 
 
- % 
 
- % input: 
 
- % C         - 系数矩阵,可以适应workers和tasks数目不同的情形 
 
- % ismin     - 1表示最小化问题,0表示最大化问题 
 
- % ouput: 
 
- % cost      - 最终花费代价 
 
- % CMatrix   - 对应的匹配矩阵,元素1所在位置c_{ij}表示j task分配给 i worker。 
 
- % 
 
- [m,n]=size(C); 
 
- if ismin==0 
 
- C=max(C(:))-C; 
 
- end 
 
- %workes 和tasks数目不相同 
 
- if m<n 
 
- C=[C;zeros(n-m,n)]; 
 
- elseif m>n 
 
- C=[C zeros(m,m-n)]; 
 
- end 
 
- copyC=C; 
 
- d=max(m,n);% 最终系数矩阵的维度 
 
- C=C-repmat(min(C,[],2),1,d); 
 
- C=C-repmat(min(C,[],1),d,1); 
 
- %% 方法一 
 
- while 1 
 
- A=int8((C==0)); 
 
- nIZeros=0;  % 独立0元素的个数 
 
- while 1 
 
- r=sum(A==1,2); % 每一行0元素的个数 
 
- [~,idr]=find(r'==1);%找到只有一个0元素的行 
 
- if ~isempty(idr) % 如果找到这样的行 
 
- tr=A(idr(1),:); 
 
- [~,idc]=find(tr==1);%找到0元素所在列 
 
- A(idr(1),idc)=2;%标注独立元素 
 
- tc=A(:,idc); 
 
- tc(idr(1))=2; 
 
- [~,idr]=find(tc'==1);%找到独立0元素所在列的其他0元素 
 
- A(idr,idc)=-2;%划掉独立0元素所在列的其余0元素 
 
- nIZeros=nIZeros+1; 
 
- else 
 
- c=sum(A==1,1); % 每一列0元素的个数 
 
- [~,idc]=find(c==1);%找到只含有一个0元素的列 
 
- if ~isempty(idc)% 找到这样的列 
 
- tc=A(:,idc(1)); 
 
- [~,idr]=find(tc'==1);%0元素所在的行 
 
- A(idr,idc(1))=2;%标注独立0元素 
 
- tr=A(idr,:); 
 
- tr(idc(1))=2; 
 
- [~,idc]=find(tr==1);%独立0元素所在行的其他0元素 
 
- A(idr,idc)=-2;%划掉独立0元素所在行的其余0元素 
 
- nIZeros=nIZeros+1; 
 
- else 
 
- break; 
 
- end 
 
- end 
 
- end 
 
- if nIZeros==d 
 
- %计算最优解 
 
- CMatrix=(A==2); 
 
- if ismin==1 
 
- cost=sum(copyC(:).*CMatrix(:)); 
 
- else 
 
- cost = sum((max(copyC(:))-copyC(:)).*CMatrix(:)); 
 
- end 
 
- CMatrix=CMatrix(1:m,1:n); 
 
- break;%找到d个独立0元素,则跳出循环 
 
- else% 独立0元素个数不足,就要找盖0线了 
 
- r=sum(A==2,2); 
 
- [~,idr]=find(r'==0);%不含有独立0元素的行 
 
- idrr=idr; 
 
- idcc=[]; 
 
- while 1 
 
- tr=A(idrr,:); 
 
- [~,idc]=find(tr==-2);%不含独立0元素的行中划掉的0元素所在列 
 
- if isempty(idc),break;end 
 
- tc=A(:,unique(idc)); 
 
- [idrr,~]=find(tc==2);%这些列中标注的0元素所在行 
 
- idr=[idr,idrr']; 
 
- idcc=[idcc,idc]; 
 
- end 
 
- idry=1:d; 
 
- idry(idr)=[];%盖0线所在的行索引 
 
- TempC=C;%存储非覆盖元素 
 
- TempC(idry,:)=[]; 
 
- TempC(:,idcc)=[]; 
 
- minUnOverlap=min(TempC(:)); 
 
- %更新系数矩阵 
 
- C=C-minUnOverlap; 
 
- C(idry,:)=C(idry,:)+minUnOverlap; 
 
- C(:,idcc)=C(:,idcc)+minUnOverlap; 
 
- end 
 
- end 
 
- %% 方法二 
 
- % while 1 
 
- %     CMatrix=zeros(d); 
 
- %     nLines=0; 
 
- %     A=(C==0); 
 
- %     idx=[]; 
 
- %     idy=[]; 
 
- %     sr=[]; 
 
- %     sc=[]; 
 
- %     while 1 
 
- %         r=sum(A,2); 
 
- %         c=sum(A,1); 
 
- %         r(sr)=0; 
 
- %         c(sc)=0; 
 
- %         trc=[r(:);c(:)]; 
 
- %         [trc,idtrc]=sort(trc,1,'ascend'); 
 
- %         [~,idn0]=find(trc'>0); 
 
- %         if ~isempty(idn0) 
 
- %             id=idtrc(idn0(1)); 
 
- %             if id>d 
 
- %                 tc=A(:,id-d); 
 
- %                 [~,idr]=find(tc'==1); 
 
- %                 A(idr(1),:)=0;  
 
- %                 nLines=nLines+1; 
 
- %                 idy=[idy,idr(1)]; 
 
- %                 CMatrix(idr(1),id-d)=1; 
 
- %                 sc=[sc,id-d]; 
 
- %             else 
 
- %                 tr=A(id,:); 
 
- %                 [~,idc]=find(tr==1); 
 
- %                 A(:,idc(1))=0; 
 
- %                 nLines=nLines+1; 
 
- %                 idx=[idx,idc(1)]; 
 
- %                 CMatrix(id,idc(1))=1; 
 
- %                 sr=[sr,id]; 
 
- %             end  
 
- %         else 
 
- %             break; 
 
- %         end 
 
- %     end 
 
- %     if nLines==d 
 
- %         if ismin 
 
- %             cost=sum(copyC(:).*CMatrix(:)); 
 
- %         else 
 
- %             cost=sum((max(copyC(:))-copyC(:)).*CMatrix(:)); 
 
- %         end 
 
- %         CMatrix=CMatrix(1:m,1:n); 
 
- %         break; 
 
- %     else 
 
- %         tempC=C; 
 
- %         tempC(idy,:)=[]; 
 
- %         tempC(:,idx)=[]; 
 
- %         minUnOverlap=min(tempC(:)); 
 
- %         C=C-minUnOverlap; 
 
- %         C(idy,:)=C(idy,:)+minUnOverlap; 
 
- %         C(:,idx)=C(:,idx)+minUnOverlap; 
 
- %     end 
 
- % end 
 
- end 
 
匈牙利算法的拓展
- workers数小于tasks数目 
 此时可以虚拟出若干个workers使个数相等,对于虚拟出的workers对于tasks的代价是相同的都是0.
- workers数目大于tasks数目 
 可以和上述类似,增加虚拟tasks,在系数矩阵中对应列元素都为0
- 最大值问题 
 找到系数矩阵中最大的元素,然后使用最大元素分别减去矩阵中元素,这样最大化问题就化为最小化问题,同样可以使用匈牙利算法。
测试
- 现在使用代码求解上述例题 

- 注意方法一中存在不足之处是必须找到仅含一个0元素的行或列,这并不科学,比如下面这个系数矩阵,就会出现死循环,所以可以寻找含有最少0元素的行或列,这就和方法二类似了,所以我没再实现。方法二可以解决下面这个极端例子 

我们来使用方法二(将代码中注释掉的部分反注释,方法一注释掉)测试下这个极端的例子

- 测试下面这个系数矩阵,匈牙利算法拓展1 

结果

- 对上个测试用例计算最大代价 

综上,相较于方法一,方法二能够处理各种状况。
分配问题与Hungarian算法的更多相关文章
- P2756 飞行员配对方案问题 二分图匹配 匈牙利算法
		题目背景 第二次世界大战时期.. 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外 ... 
- R+NLP︱text2vec包——四类文本挖掘相似性指标 RWMD、cosine、Jaccard 、Euclidean (三,相似距离)
		要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗"贝壳"~ --------------------------- 在之前的开篇提到了text2vec ... 
- 多目标跟踪(MOT)论文随笔-POI: Multiple Object Tracking with High Performance Detection and Appearance Feature
		网上已有很多关于MOT的文章,此系列仅为个人阅读随笔,便于初学者的共同成长.若希望详细了解,建议阅读原文. 本文是tracking by detection 方法进行多目标跟踪的文章,最大的特点是使用 ... 
- 多目标跟踪(MOT)论文随笔-SIMPLE ONLINE AND REALTIME TRACKING (SORT)
		网上已有很多关于MOT的文章,此系列仅为个人阅读随笔,便于初学者的共同成长.若希望详细了解,建议阅读原文. 本文是使用 tracking by detection 方法进行多目标跟踪的文章,是后续de ... 
- 论文阅读笔记二十二:End-to-End Instance Segmentation with Recurrent Attention(CVPR2017)
		论文源址:https://arxiv.org/abs/1605.09410 tensorflow 代码:https://github.com/renmengye/rec-attend-public 摘 ... 
- JVM有哪些垃圾回收器
		JVM 的垃圾回收器 目录 JVM 的垃圾回收器 经典垃圾收集器 Serial 收集器 ParNew 收集器 Parallel Scavenge 收集器 Serial Old 收集器 Parallel ... 
- Sparse R-CNN: End-to-End Object Detection with Learnable Proposals 论文解读
		前言 事实上,Sparse R-CNN 很多地方是借鉴了去年 Facebook 发布的 DETR,当时应该也算是惊艳众人.其有两点: 无需 nms 进行端到端的目标检测 将 NLP 中的 Transf ... 
- CVPR2021 | Transformer用于End-to-End视频实例分割
		 论文:End-to-End Video Instance Segmentation with Transformers 获取:在CV技术指南后台回复关键字"0005"获取该论文 ... 
- 51nod贪心算法入门-----任务分配问题
		任务执行顺序 有N个任务需要执行,第i个任务计算时占R[i]个空间,而后会释放一部分,最后储存计算结果需要占据O[i]个空间(O[i] < R[i]). 分析: 可以抽象成,从一个整数开始,每次 ... 
随机推荐
- PHP WAMP 文件上传 及 简单的上传预览
			...... 使用特殊的表单类型file, 主(上传)页面: <form action="chuli.php" method="post" enctype ... 
- Spring JdbcTemplate 方法详解
			JdbcTemplate主要提供以下五类方法: execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句: update方法及batchUpdate方法:update方法用于执行新增.修 ... 
- ArcGIS Server开发教程系列(3)切片
			切片工作,我们可以一级一级的切,也可以,所有的一块切,Recreate All Tiles这项是说,在没有进行任何的切片工作时,可以选用这项:Recreate Empty Tiles这项是说,如果之前 ... 
- 获取centos6.5系统信息脚本
			最近想尝试做两件比较重要的事情,第一是用python写个cmdb,第二还是用python写个小型监控系统,下面是获取系统信息的脚本: #!/usr/bin/env python # coding:ut ... 
- 查看修改Linux时区和时间
			查看/修改Linux时区和时间 一.时区 1. 查看当前时区 date -R 2. 修改设置时区 方法(1) tzselect 方法(2) 仅限于RedHat Linux 和 CentOS timec ... 
- Android高手速成--第二部分 工具库
			主要包括那些不错的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多媒体相关及其他. 一.依赖注入DI 通过依赖注入减少Vie ... 
- 浅谈JSON
			JSON的全称是”JavaScript Object Notation”,意思是JavaScript对象表示法,它是一种基于文本,独立于语言的轻量级数据交换格式.XML也是一种数据交换格式,为什么没有 ... 
- PHP常用字符串的操作函数
			字符串转换类函数 addcslashes函数:以C语言风格使用反斜线转义字符串中的字符 addslashes函数:使用反斜线引用字符串 chop函数:清除字符串中的连续空格 get_html_tran ... 
- 优化MySQL数据库性能的八大方法
			本文探讨了提高MySQL 数据库性能的思路,并从8个方面给出了具体的解决方法. 1.选取最适用的字段属性 MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就 ... 
- CentOS6.3 编译安装LAMP(2):编译安装 Apache2.2.25
			所需源码包: /usr/local/src/Apache-2.2.25/httpd-2.2.25.tar.gz 编译安装 Apache2.2.25 #切换到源码目录 cd /usr/local/src ... 
