分配问题与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五项任务,每个员工完成某项任务的时间如下图所示,应该如何分配任务,才能保证完成任务所需要的时间开销最小?

1476015762594.jpg

解:

  1. 写出系数矩阵

  2. 更新系数矩阵,使系数矩阵的每一行每一列都减去该行该列的最小值,保证每一行每一列都有0元素出现,参见定理2.

  3. 选择只有一个0元素的行或列将该0元素标注为独立0元素,并将该0元素所在的列或行中0元素划掉,直至找不到满足条件的行或列,需要注意的是在循环时,划掉的0元素不再视为0元素。比如我们找下面这个系数矩阵的标注0元素和划掉的0元素

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

可以发现我们在标注第一行第2个0元素时,并没有把已经划掉的第一个0当作0元素看待。

  1. 划盖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更新系数矩阵。

  2. 更新系数矩阵。

    a. 找到未被盖0线覆盖的元素中的最小值

    b. 所有未被盖0线覆盖的元素减去最小值

    c. 所有盖0线交叉处的元素值加上最小值

  3. 重复步骤4,5

  4. 计算最优解。

    类似步骤3中找独立0元素的方法在C中找到n个不同行不同列的0元素所对应的位置。

定理1. 系数矩阵C中独立零元素的最多个数等于能覆盖所有零元素的最少线数。 —— D. Konig

定理2. 若将分配问题的系数矩阵每一行及每一列分别减去各行及各列的最小元素,则新的分配问题与原分配问题有相同的最优解,只是最优值差一个常数。

当然,注意找盖〇线的方法并不是唯一的,比如下述方法

  1. 同上一方法

  2. 同上一方法

  3. 在C中寻找未被盖〇线覆盖的存在0元素且数组最少的行(列),标注一个0元素作为独立0元素,该0元素所在的列(行)做盖0线。重复此步骤,直至找不到符合条件的行或列,已经被找过的行(列)就不再找了!

  4. 若是盖0线数组等于矩阵维度,则跳到7

  5. 同上一方法

  6. 重复3,4,5

  7. 同上一方法

当然还有别的方法,比如从包含最多0元素的行或列开始做盖0线直到将所有的0元素覆盖掉等

这里代码实现了上面两个方法,注释掉的是第二个方法的核心

  1. function [cost,CMatrix]=Assignment(C,ismin) 

  2. % Assignment problem solved by hungarian method. 



  3. % input: 

  4. % C - 系数矩阵,可以适应workers和tasks数目不同的情形 

  5. % ismin - 1表示最小化问题,0表示最大化问题 

  6. % ouput: 

  7. % cost - 最终花费代价 

  8. % CMatrix - 对应的匹配矩阵,元素1所在位置c_{ij}表示j task分配给 i worker。 




  9. [m,n]=size(C); 

  10. if ismin==0 

  11. C=max(C(:))-C; 

  12. end 


  13. %workes 和tasks数目不相同 

  14. if m<n 

  15. C=[C;zeros(n-m,n)]; 

  16. elseif m>n 

  17. C=[C zeros(m,m-n)]; 

  18. end 

  19. copyC=C; 

  20. d=max(m,n);% 最终系数矩阵的维度 

  21. C=C-repmat(min(C,[],2),1,d); 

  22. C=C-repmat(min(C,[],1),d,1); 


  23. %% 方法一 

  24. while 1 

  25. A=int8((C==0)); 

  26. nIZeros=0; % 独立0元素的个数 

  27. while 1 

  28. r=sum(A==1,2); % 每一行0元素的个数 

  29. [~,idr]=find(r'==1);%找到只有一个0元素的行 

  30. if ~isempty(idr) % 如果找到这样的行 

  31. tr=A(idr(1),:); 

  32. [~,idc]=find(tr==1);%找到0元素所在列 

  33. A(idr(1),idc)=2;%标注独立元素 

  34. tc=A(:,idc); 

  35. tc(idr(1))=2; 

  36. [~,idr]=find(tc'==1);%找到独立0元素所在列的其他0元素 

  37. A(idr,idc)=-2;%划掉独立0元素所在列的其余0元素 

  38. nIZeros=nIZeros+1; 

  39. else 

  40. c=sum(A==1,1); % 每一列0元素的个数 

  41. [~,idc]=find(c==1);%找到只含有一个0元素的列 

  42. if ~isempty(idc)% 找到这样的列 

  43. tc=A(:,idc(1)); 

  44. [~,idr]=find(tc'==1);%0元素所在的行 

  45. A(idr,idc(1))=2;%标注独立0元素 

  46. tr=A(idr,:); 

  47. tr(idc(1))=2; 

  48. [~,idc]=find(tr==1);%独立0元素所在行的其他0元素 

  49. A(idr,idc)=-2;%划掉独立0元素所在行的其余0元素 

  50. nIZeros=nIZeros+1; 

  51. else 

  52. break; 

  53. end 

  54. end 

  55. end 


  56. if nIZeros==d 

  57. %计算最优解 

  58. CMatrix=(A==2); 


  59. if ismin==1 

  60. cost=sum(copyC(:).*CMatrix(:)); 

  61. else 

  62. cost = sum((max(copyC(:))-copyC(:)).*CMatrix(:)); 

  63. end 

  64. CMatrix=CMatrix(1:m,1:n); 

  65. break;%找到d个独立0元素,则跳出循环 

  66. else% 独立0元素个数不足,就要找盖0线了 

  67. r=sum(A==2,2); 

  68. [~,idr]=find(r'==0);%不含有独立0元素的行 

  69. idrr=idr; 

  70. idcc=[]; 

  71. while 1 

  72. tr=A(idrr,:); 

  73. [~,idc]=find(tr==-2);%不含独立0元素的行中划掉的0元素所在列 

  74. if isempty(idc),break;end 

  75. tc=A(:,unique(idc)); 

  76. [idrr,~]=find(tc==2);%这些列中标注的0元素所在行 

  77. idr=[idr,idrr']; 

  78. idcc=[idcc,idc]; 

  79. end 

  80. idry=1:d; 

  81. idry(idr)=[];%盖0线所在的行索引 

  82. TempC=C;%存储非覆盖元素 

  83. TempC(idry,:)=[]; 

  84. TempC(:,idcc)=[]; 

  85. minUnOverlap=min(TempC(:)); 

  86. %更新系数矩阵 

  87. C=C-minUnOverlap; 

  88. C(idry,:)=C(idry,:)+minUnOverlap; 

  89. C(:,idcc)=C(:,idcc)+minUnOverlap; 

  90. end 

  91. end 

  92. %% 方法二 

  93. % while 1 

  94. % CMatrix=zeros(d); 

  95. % nLines=0; 

  96. % A=(C==0); 

  97. % idx=[]; 

  98. % idy=[]; 

  99. % sr=[]; 

  100. % sc=[]; 

  101. % while 1 

  102. % r=sum(A,2); 

  103. % c=sum(A,1); 

  104. % r(sr)=0; 

  105. % c(sc)=0; 

  106. % trc=[r(:);c(:)]; 

  107. % [trc,idtrc]=sort(trc,1,'ascend'); 

  108. % [~,idn0]=find(trc'>0); 

  109. % if ~isempty(idn0) 

  110. % id=idtrc(idn0(1)); 

  111. % if id>d 

  112. % tc=A(:,id-d); 

  113. % [~,idr]=find(tc'==1); 

  114. % A(idr(1),:)=0;  

  115. % nLines=nLines+1; 

  116. % idy=[idy,idr(1)]; 

  117. % CMatrix(idr(1),id-d)=1; 

  118. % sc=[sc,id-d]; 

  119. % else 

  120. % tr=A(id,:); 

  121. % [~,idc]=find(tr==1); 

  122. % A(:,idc(1))=0; 

  123. % nLines=nLines+1; 

  124. % idx=[idx,idc(1)]; 

  125. % CMatrix(id,idc(1))=1; 

  126. % sr=[sr,id]; 

  127. % end  

  128. % else 

  129. % break; 

  130. % end 

  131. % end 

  132. % if nLines==d 

  133. % if ismin 

  134. % cost=sum(copyC(:).*CMatrix(:)); 

  135. % else 

  136. % cost=sum((max(copyC(:))-copyC(:)).*CMatrix(:)); 

  137. % end 

  138. % CMatrix=CMatrix(1:m,1:n); 

  139. % break; 

  140. % else 

  141. % tempC=C; 

  142. % tempC(idy,:)=[]; 

  143. % tempC(:,idx)=[]; 

  144. % minUnOverlap=min(tempC(:)); 

  145. % C=C-minUnOverlap; 

  146. % C(idy,:)=C(idy,:)+minUnOverlap; 

  147. % C(:,idx)=C(:,idx)+minUnOverlap; 

  148. % end 

  149. % end 


  150. end 


匈牙利算法的拓展

  • workers数小于tasks数目

    此时可以虚拟出若干个workers使个数相等,对于虚拟出的workers对于tasks的代价是相同的都是0.

  • workers数目大于tasks数目

    可以和上述类似,增加虚拟tasks,在系数矩阵中对应列元素都为0

  • 最大值问题

    找到系数矩阵中最大的元素,然后使用最大元素分别减去矩阵中元素,这样最大化问题就化为最小化问题,同样可以使用匈牙利算法。

测试

  1. 现在使用代码求解上述例题

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

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

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

1476017394268.jpg

结果

1476017434080.jpg
  1. 对上个测试用例计算最大代价

1476017934004.jpg

综上,相较于方法一,方法二能够处理各种状况。

分配问题与Hungarian算法的更多相关文章

  1. P2756 飞行员配对方案问题 二分图匹配 匈牙利算法

    题目背景 第二次世界大战时期.. 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1名是外 ...

  2. R+NLP︱text2vec包——四类文本挖掘相似性指标 RWMD、cosine、Jaccard 、Euclidean (三,相似距离)

    要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗"贝壳"~ --------------------------- 在之前的开篇提到了text2vec ...

  3. 多目标跟踪(MOT)论文随笔-POI: Multiple Object Tracking with High Performance Detection and Appearance Feature

    网上已有很多关于MOT的文章,此系列仅为个人阅读随笔,便于初学者的共同成长.若希望详细了解,建议阅读原文. 本文是tracking by detection 方法进行多目标跟踪的文章,最大的特点是使用 ...

  4. 多目标跟踪(MOT)论文随笔-SIMPLE ONLINE AND REALTIME TRACKING (SORT)

    网上已有很多关于MOT的文章,此系列仅为个人阅读随笔,便于初学者的共同成长.若希望详细了解,建议阅读原文. 本文是使用 tracking by detection 方法进行多目标跟踪的文章,是后续de ...

  5. 论文阅读笔记二十二:End-to-End Instance Segmentation with Recurrent Attention(CVPR2017)

    论文源址:https://arxiv.org/abs/1605.09410 tensorflow 代码:https://github.com/renmengye/rec-attend-public 摘 ...

  6. JVM有哪些垃圾回收器

    JVM 的垃圾回收器 目录 JVM 的垃圾回收器 经典垃圾收集器 Serial 收集器 ParNew 收集器 Parallel Scavenge 收集器 Serial Old 收集器 Parallel ...

  7. Sparse R-CNN: End-to-End Object Detection with Learnable Proposals 论文解读

    前言 事实上,Sparse R-CNN 很多地方是借鉴了去年 Facebook 发布的 DETR,当时应该也算是惊艳众人.其有两点: 无需 nms 进行端到端的目标检测 将 NLP 中的 Transf ...

  8. CVPR2021 | Transformer用于End-to-End视频实例分割

    ​ 论文:End-to-End Video Instance Segmentation with Transformers 获取:在CV技术指南后台回复关键字"0005"获取该论文 ...

  9. 51nod贪心算法入门-----任务分配问题

    任务执行顺序 有N个任务需要执行,第i个任务计算时占R[i]个空间,而后会释放一部分,最后储存计算结果需要占据O[i]个空间(O[i] < R[i]). 分析: 可以抽象成,从一个整数开始,每次 ...

随机推荐

  1. vim编辑器的使用

    I 在光标所在行的行首插入 A 在光标所在行的行尾插入 { 移动到上一段 } 移动到下一段 空格向后移动一格 H 屏幕顶部 M 屏幕中间 L 屏幕下方 n| 使光标移动到第几个字符处 ngg 移动到制 ...

  2. Python Day8

    Socket Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型 ...

  3. Pjax调用

    $.pjax({container:'#content_center',url:href,data:data}); $(document).on('pjax:send', function() {// ...

  4. 面试题目——《CC150》递归与动态规划

    面试题9.1:有个小孩正在上楼梯,楼梯有n个台阶,小孩一次可以上1阶.2阶或者3阶.实现一个方法,计算小孩有多少种上楼梯的方式. 思路:第4个数是前三个数之和 注意:能不能使用递归,能不能建立一个很大 ...

  5. 查看修改Linux时区和时间

    查看/修改Linux时区和时间 一.时区 1. 查看当前时区 date -R 2. 修改设置时区 方法(1) tzselect 方法(2) 仅限于RedHat Linux 和 CentOS timec ...

  6. 【08-23】redis学习笔记

    今天开始重拾linux,使用的是ubuntu发行版,主要是想在linux上学习redis,作为服务器端软件天然选择linux啊. 第一次使用ubuntu配置超级管理员密码: su passwd roo ...

  7. Coursera-Getting and Cleaning Data-Week3-dplyr+tidyr+lubridate的组合拳

    Coursera-Getting and Cleaning Data-Week3 Wednesday, February 04, 2015 好久不写笔记了,年底略忙.. Getting and Cle ...

  8. [Kerberos] User Ticket Management

    Kerberos客户端常用命令包括 kinit, klist, kdestroy, and kpasswd,用户使用这些命令管理自己的 ticket. 此外,每台运行Kerberos的机器应该都配置/ ...

  9. GDI+ 笔记

    1.GDI+模板 #include<windows.h> #include<GdiPlus.h> #include <time.h> #include <ma ...

  10. jq的$()里面 一定要是字符串类型!!!!!!!!!!!!!!!!!!!!!!!!

    var s = "[value="+uid+"]"; $(s).attr("checked",'true');