分配问题与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. % 

  4. % input: 

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

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

  7. % ouput: 

  8. % cost - 最终花费代价 

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

  10. % 


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

  12. if ismin==0 

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

  14. end 


  15. %workes 和tasks数目不相同 

  16. if m<n 

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

  18. elseif m>n 

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

  20. end 

  21. copyC=C; 

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

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

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


  25. %% 方法一 

  26. while 1 

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

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

  29. while 1 

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

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

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

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

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

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

  36. tc=A(:,idc); 

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

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

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

  40. nIZeros=nIZeros+1; 

  41. else 

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

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

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

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

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

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

  48. tr=A(idr,:); 

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

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

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

  52. nIZeros=nIZeros+1; 

  53. else 

  54. break; 

  55. end 

  56. end 

  57. end 


  58. if nIZeros==d 

  59. %计算最优解 

  60. CMatrix=(A==2); 


  61. if ismin==1 

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

  63. else 

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

  65. end 

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

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

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

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

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

  71. idrr=idr; 

  72. idcc=[]; 

  73. while 1 

  74. tr=A(idrr,:); 

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

  76. if isempty(idc),break;end 

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

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

  79. idr=[idr,idrr']; 

  80. idcc=[idcc,idc]; 

  81. end 

  82. idry=1:d; 

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

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

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

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

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

  88. %更新系数矩阵 

  89. C=C-minUnOverlap; 

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

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

  92. end 

  93. end 

  94. %% 方法二 

  95. % while 1 

  96. % CMatrix=zeros(d); 

  97. % nLines=0; 

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

  99. % idx=[]; 

  100. % idy=[]; 

  101. % sr=[]; 

  102. % sc=[]; 

  103. % while 1 

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

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

  106. % r(sr)=0; 

  107. % c(sc)=0; 

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

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

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

  111. % if ~isempty(idn0) 

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

  113. % if id>d 

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

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

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

  117. % nLines=nLines+1; 

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

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

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

  121. % else 

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

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

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

  125. % nLines=nLines+1; 

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

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

  128. % sr=[sr,id]; 

  129. % end  

  130. % else 

  131. % break; 

  132. % end 

  133. % end 

  134. % if nLines==d 

  135. % if ismin 

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

  137. % else 

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

  139. % end 

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

  141. % break; 

  142. % else 

  143. % tempC=C; 

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

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

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

  147. % C=C-minUnOverlap; 

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

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

  150. % end 

  151. % end 


  152. 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. JAVA教师:给JAVA初学者的忠告

    我带过不少JAVA,C++班的课,来学习的同学很多都是初学者,一部分是急着找工作的,一部分是很感兴趣的.他们都想在短短一两个星期内掌握Java,这是不切实际的.而且这样做很容易让自己心浮气燥,难以静下 ...

  2. Base64原理

    一.Base64编码由来 为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就 不能通过邮件传送.这样用途就受到了很大的 ...

  3. 常用的Firefox浏览器插件、Chrome浏览器插件收藏

    [血的教训] 不要去下载“Firefox中国版(谋智网络)”,默认情况下会给你安装好多的莫名其妙的插件,推荐去Firefox官方下载原版. Firefox 原版官方网址: https://www.mo ...

  4. 更改CentOS 6.3 yum源为国内163源

    CentOS5.x: http://mirrors.163.com/.help/CentOS5-Base-163.repo CentOS6.x: http://mirrors.163.com/.hel ...

  5. css特殊字符总结

    < :    &lt > :    &gt

  6. java后端制作MD5加密

    由于一次业务的需要,我制作了一次密码的修改子业务. 当用户忘记密码的情况下,我需要动态的发给他一个6位的随机密码,通过即时通,短信,微信等.并同时修改数据库中的原密码为这6位的随机密码.让用户再去修改 ...

  7. python匹配ip正则

    python匹配ip正则 #!/usr/bin/env python # -*- coding:utf-8 -*- import re ip_str = "asdad1.1.1.1sdfwe ...

  8. 在 IIS 6 和 IIS 7中配置Https,设置WCF同时支持HTTP和HTPPS,以及使用HttpWebRequest和HttpClient调用HttpS

    IIS 7 ,给IIS添加CA证书以支持https IIS 6 架设证书服务器 及 让IIS启用HTTPS服务 WCF IIS 7中配置HTTPS C#利用HttpWebRequest进行post请求 ...

  9. C++中的 :: 用法

    ::是运算符中等级最高的,它分为三种:1)global scope(全局作用域符),用法(::name)2)class scope(类作用域符),用法(class::name)3)namespace ...

  10. js 连续赋值。。理解不了,先占坑

    http://www.cnblogs.com/xxcanghai/p/4998076.html