匈牙利算法与KM算法

  1. 匈牙利算法的概述

    用来解决二分图中的最优分配问题的算法,也就是图论中寻找最大匹配的算法。

  2. 从实际问题的角度理解(\(\mbox{KM}\)算法)

    1. 第(1)步:找到每个成员的长处,即寻找各个成员完成各任务的最短耗时。将成本矩阵的各行减去该行的最小值,找出各行的“0”;

    2. 第(2)步:找到各任务的最佳人选,即寻找各任务分配给各成员完成的最短耗时。将(1)中处理后的成本矩阵的各列减去该列的最小值,找出各列的“0”;

    3. 第(3)步:成员和任务的礼让原则。相对不能“将就”的成员和任务优先选择。

      1. 成员礼让原则。寻找长处最少的成员,先把他的长处分配给他,因为长处越多的成员越容易调配。在(2)中处理后的成本矩阵中找到含有“0”最少的行,然后将该行的“0”画上圈(记为\(\circledcirc\)),同时由于一个任务同一时刻只能由一个成员完成,故划去其所在列的其他“0”(记为\(\phi\));
      2. 任务礼让原则。寻找最佳成员最少的任务,先把它的最佳成员分配给它,原因同(3)。在(3)中处理后的成本矩阵中找到含有“0”最少的列,然后将该列的“0”画上圈(记为\(\circledcirc\)),同时由于一个成员同一时刻只能完成一个任务,故划去其所在行的其他“0”(记为\(\phi\));
    4. 第(4)步:寻找牺牲的成员和任务。方法为打钩划线法。

      1. 打钩。对(3)中处理后的成本矩阵中不含“\(\circledcirc\)”的行打钩(寻找牺牲成员\(M_1\)),然后对该行中含有“\(\phi\)”的列打钩(寻找该成员与其他成员相冲突的任务\(T_1\)),再对该打钩列中含有“\(\circledcirc\)”的行打钩(寻找完成该冲突任务\(T_1\)的其他成员\(M_2\))。若\(M_2\)中还有与其他成员冲突的任务,则重复上述步骤,反之则结束打钩过程;
      2. 划线。寻找有任务冲突的所有成员,然后提取出他们无冲突的任务,为之后的重新分配)做准备。对未打钩的行(无任务冲突的成员)和已打钩的列(冲突任务)划线排除。
    5. 第(5)步:重新调度分配。找到所有有任务冲突的成员,标记出他们的无冲突任务,最后选择耗时最少的任务作为“激励”/“惩罚”因子。在(4)中处理后的成本矩阵中寻找未被划线元素的最小值,然后对打钩行减去这个最小值、对打钩列增加这个最小值。

      1. 对打钩行减去这个最小值:在增加“0”的同时也能对冲突成员的无冲突任务进行“激励”。如果这次分配失败,则下一次分配时这些任务会更先被分配;
      2. 对打钩列增加这个最小值:在保证任务耗时非负的同时也能对有冲突任务进行“惩罚”。如果这次分配失败,则下一次分配时这些任务会更后被分配;
    6. 第(6)步:如果出现各行各列都只有一个“\(\circledcirc\)”则说明分配成功,否则分配失败,重复(4)~(6)。

  3. 从图论理论的角度理解(匈牙利算法)

    1. 专有名词解释

      1. 匹配\(\mbox{M}\):是边集\(\mbox{E}\)的一个无环子集,且它的任意两条边在图\(\mbox{G}\)中都不相邻;
      2. \(\mbox{M}\)的饱和顶点\(\nu\)\(\exists{e}\in{M}\)与顶点\(\nu\)关联;
      3. \(\mbox{M}\)的非饱和顶点\(\nu\)\(\forall{e}\in{M}\)不与顶点\(\nu\)关联;
      4. 最大匹配\(\mbox{M}\):所有匹配中边数最多的匹配方案;
      5. 完美匹配\(\mbox{M}\):某个匹配包含了图\(\mbox{G}\)中所有的顶点;
    2. 交错路径

      \(M\)是图\(G\)某匹配,若某路径在\(M\)\(G-M\)中交替出现,则将这条路径称为\(M\)交错路径。

    3. 增广路径

      如果一条\(M\)交错路径的起点和终点都是\(M\)非饱和顶点,则称为\(M\)增广路径。

    4. 算法步骤

      img
    5. 我的理解

      1. 算法的目标

        任取一个\(M(X)\)非饱和顶点\(x_0\)作为“入射源点”,经过或者不经过\(M\)匹配中的“反射”,最后需要找到一个\(M(Y)\)非饱和顶点\(y_0\)作为“出射终点”,这样一条“反射光线”被称为“\(M\)增广路径”。

      2. 算法的遍历思想

        有点儿类似于层序遍历的思想。将\(N(S)\)作为访问备选方案,将其中已经访问过的\(Y\)做上标记(用集合\(T\)表示),以便后续不再重复访问(\(y\in{N(S)-T}\))。

        在访问\(Y\)中结点之前必须对\(N(S)\)做判断,查看是否全被访问过(即\(N(S)\subseteq{T}\)),而对于每一个被访问的\(Y\)结点都会判断其是否属于\(M(Y)\)饱和顶点:

        1. 如果属于\(M(Y)\)饱和顶点:将与之\(M\)匹配的\(X_i\)并入\(S\),并将\(y\)并入访问标记\(T\)中;
        2. 如果不属于\(M(Y)\)饱和顶点:必然存在一条增广路径\(P(x_0,y_0)\)\(M=M\oplus{E(P)}\)
      3. 如何获取增广路径

        可以用\(\mbox{DFS}\)(深度优先搜索)算法。

  4. 赋权二部图最大匹配\(\mbox{Kuhn-Munkres}\)算法

    1. 顶点标号与可行顶点标号

      \(G\)的顶点标号\(l\)是从顶点集到正整数集的一个映射。用\(l(\nu)\)\(G\)上顶点\(\nu\)的标号,边\(<\nu_1,\nu_2>\)的权重用\(\omega(\nu_1\nu_2)\)表示。如果对于赋权二部图\(G\)中的每一条边\(<x,y>\)都有\(l(x)+l(y)\ge\omega(xy)\),则称这个标号\(l\)是图\(G\)的一个可行顶点标记。

      其意义在于权重\(\omega(xy)\)是固定的,只有顶点的标号是随意初始化的。

      每次访问\(y\)都需要对其施加“惩罚”(\(+\)\(\alpha_l\),而对与之匹配的\(x\)施加“激励”(\(-\)\(\alpha_l\)

      通常选择\(l(x_i)=\underset{y\in{Y}}{\max}{(\omega(xy))}\)\(l(y_i)=0\),然后通过不断地调整\(l(x)\)\(l(y)\)使得\(l(x)+l(y)\rightarrow\omega(xy)\)

    2. 相等子图的概念以及推论

      1. 相等子图

        \(E_l=\{xy\in{E(G)|l(x)+l(y)=\omega(xy)}\}\)(也就是匹配),则\(G(E_l)\)\(G\)\(l\)相等子图\(G_l\)

      2. 定理推论

        相等子图\(G_l\)的完美匹配,亦是\(G\)的最大权完美匹配。

    3. \(\mbox{KM}\)算法优化方法及步骤

      1. 如果\(\norm{X}\neq\norm{Y}\),则给\(G(X,Y)\)添加一些顶点和权为0的边,使其成为赋权二部图。

      2. \(G\)中任意一可行顶点标号开始,求出相等子图\(G_l\)(初始化一个匹配\(M\))。

      3. \(G_l\)中执行匈牙利算法,观察是否能够输出一个完美匹配\(M\)

        1. 如果可以求得完美匹配\(M\),则将其输出并停止;

        2. 否则重新对顶点标号赋值:所有访问过的\(y\)都需要对其施加“惩罚”(\(+\)\(\alpha_l\),而与它们匹配的\(x\)施加“激励”(\(-\)\(\alpha_l\),其余的顶点标号不变。

          \[\alpha_l\overset{\Delta}{=}\underset{x\in{S},y\notin{T}}{\min}{\{l(x)+l(y)-\omega(xy)\}},其中S=\mbox{visited}_x,T=\mbox{visited}_y \]

          也就是说使得\(\mbox{visited}_{x}\)有更多的\(y\)与之匹配,而限制\(\mbox{visited}_y\)的匹配数量。

posted @ 2022-09-01 13:03  BNTU  阅读(277)  评论(0编辑  收藏  举报