KM算法的实现和封装

自己在写一个小程序时,遇到了一个类似于“完备匹配下的最大权匹配”的优化问题。在网上搜了下相关资料,了解到对应的匈牙利算法与KM算法,而且已经都有大神进行了详细讲解和代码的编写。唯一的不同之处是我参考的文章中KM算法目标是匹配结果最大为目标,而我的程序中是以匹配结果最小为目标。自己把代码改写了下,并封装为类。验证结果表明代码没有问题~

这里将代码放出,以供网友参考。不过具体到算法层,本人理解不深,还是观摩网上算法大神的讲解吧。

代码如下:

KM.h

 1 #ifndef _KM_H_
 2 #define _KM_H_
 3 
 4 #include <vector>
 5 
 6 
 7 class KM
 8 {
 9 public:
10     KM();
11     ~KM();
12     void init(int size, std::vector<std::vector<double>> diff);
13     bool dfs(int a);
14     void KM_Calc(); 
15     int* getMatch();
16     double getdiffSum();
17 
18 private:
19     std::vector<std::vector<double>> diff;// 差异度矩阵(方阵)
20     double* ex_a;// 每个a类对象的期望
21     double* ex_b;// 每个b类对象的期望
22     bool* vis_a;// 记录每一轮匹配过的a类对象
23     bool* vis_b;// 记录每一轮匹配过的b类对象
24     int* match;// 记录每一个b类对象匹配到的a类对象索引,如果没有匹配到则为-1
25     double* slack;// 记录每个b类对象能被a类对象匹配到所需要减少的最小差异度
26     int size;// 记录差异度矩阵的边长
27     double diffSum;// 记录匹配结果的总体差异度大小
28 };
29 
30 
31 
32 #endif
View Code

KM.cpp

  1 #include "KM.h"
  2 
  3 KM::KM()
  4 {
  5     // 参数初始化
  6     ex_a = ex_b = NULL;
  7     slack = NULL;
  8     match = NULL;
  9     vis_a = vis_b = NULL;
 10     size = 0;
 11     diffSum = 0;
 12 }
 13 
 14 KM::~KM()
 15 {
 16     /* 释放之前分配了的内存 */
 17     if(ex_a)
 18     {
 19         delete []ex_a;
 20         ex_a = NULL;
 21     }
 22     if(ex_b)
 23     {
 24         delete []ex_b;
 25         ex_b = NULL;
 26     }
 27     if(vis_a)
 28     {
 29         delete []vis_a;
 30         vis_a = NULL;
 31     }
 32     if(vis_b)
 33     {
 34         delete []vis_b;
 35         vis_b = NULL;
 36     }
 37     if(slack)
 38     {
 39         delete []slack;
 40         slack = NULL;
 41     }
 42 
 43     if(match)
 44     {
 45         delete []match;
 46         match = NULL;
 47     }
 48     
 49 }
 50 
 51 void KM::init(int size, std::vector<std::vector<double>> diff)
 52 {
 53     /* 获取差异度矩阵,根据矩阵的边长为其他变量分配空间 */
 54     this->diff = diff;
 55     this->size = size;
 56     ex_a = new double[size];
 57     ex_b = new double[size];
 58     vis_a = new bool[size];
 59     vis_b = new bool[size];
 60     slack = new double[size];
 61     match = new int[size];
 62 }
 63 
 64 bool KM::dfs(int a)
 65 {
 66     vis_a[a] = true;
 67     for(int b = 0; b < size; b++)
 68     {
 69         if(vis_b[b])// 每一轮匹配中b类每个对象只尝试一次
 70             continue;
 71         double c = diff[a][b];
 72         double gap = ex_a[a] + ex_b[b] - c;// 差异度符合要求
 73         if(fabs(gap) < 1e-6)
 74         {
 75             vis_b[b] = true;
 76             if(match[b] == -1 || dfs(match[b]))// 匹配成功
 77             {
 78                 match[b] = a;
 79                 return true;
 80             }
 81         }
 82         else
 83         {
 84             slack[b] = std::max(slack[b], gap);// 匹配失败,记录该b类对象若想成功匹配,其差异度减小的最小值
 85         }
 86     }
 87 
 88     return false;
 89 }
 90 
 91 void KM::KM_Calc()
 92 {
 93     for(int i = 0; i < size; i++)
 94     {
 95         ex_b[i] = 0;// 每个b类对象的差异度初始为0
 96         match[i] = -1;
 97         ex_a[i] = (diff)[i][0];
 98         for(int j = 1; j < size; j++)
 99         {
100             ex_a[i] = std::min(ex_a[i], (diff)[i][j]);// 每个a类对象的初始期望值是与之相连的每个b类对象差异度的最小值
101         }
102 
103     }
104 
105     for(int i = 0; i < size; i++)
106     {
107         for(int m = 0; m < size; m++)
108             slack[m] = -1000000.0;
109         while(1)
110         {
111             for(int m = 0; m <size; m++)
112             {
113                 vis_a[m] = false;
114                 vis_b[m] = false;
115             }
116             if(dfs(i))
117                 break;// 匹配成功,退出
118             // 匹配失败
119             double d = -1000000.0;
120             for(int j = 0; j < size; j++)
121             {
122                 if(!vis_b[j])
123                     d = std::max(d, slack[j]);// 注意这里d小于0
124             }
125             for(int j = 0; j < size; j++)
126             {
127                 if(vis_a[j])
128                     ex_a[j] -= d;// 参加过匹配的a类对象只能提高差异度的期望值
129                 if(vis_b[j])
130                     ex_b[j] += d;// 参加过匹配的b类对象有资格“要求”a类对象降低差异度
131                 else
132                     slack[j] -= d;
133             }
134         }
135     }
136     
137     diffSum = 0;
138     // 计算匹配结果的总差异度
139     for(int i = 0; i < size; i++)
140     {
141         diffSum += (diff)[match[i]][i];
142     }
143 }
144 
145 int* KM::getMatch()
146 {
147     return match;
148 }
149 
150 double KM::getdiffSum()
151 {
152     return diffSum;
153 }
View Code

main.cpp提供调用示例,其中的数据来自http://blog.csdn.net/kevinjqy/article/details/54584114

 1 #include <iostream>
 2 
 3 #include "km.h"
 4 
 5 int main()
 6 {
 7     /* 构造差异度矩阵 */
 8     std::vector<std::vector<double>> diff;
 9     diff.resize(4);
10     for(int i = 0; i < diff.size(); i++)
11     {
12         diff[i].resize(4);
13     }
14     diff[0][0] = 90;
15     diff[0][1] = 75;
16     diff[0][2] = 75;
17     diff[0][3] = 80;
18     diff[1][0] = 35;
19     diff[1][1] = 85;
20     diff[1][2] = 55;
21     diff[1][3] = 65;
22     diff[2][0] = 125;
23     diff[2][1] = 95;
24     diff[2][2] = 90;
25     diff[2][3] = 105;
26     diff[3][0] = 45;
27     diff[3][1] = 110;
28     diff[3][2] = 95;
29     diff[3][3] = 115;
30 
31     /* 初始化算法对象 */
32     KM km;
33     km.init(diff.size(), diff);
34     /* 计算结果并输出 */
35     km.KM_Calc();
36     std::cout<<"diffSum: "<<km.getdiffSum()<<std::endl;
37     int* match = km.getMatch();
38     for(int i = 0; i < diff.size(); i++)
39     {
40         std::cout<<match[i]<<" ";
41     }
42     std::cout<<std::endl;
43 
44     return 0;
45 }
View Code

参考文章:

http://www.cnblogs.com/wenruo/p/5264235.html KM算法详解+模板

http://blog.csdn.net/kevinjqy/article/details/54584114 分配问题与匈牙利算法

http://blog.csdn.net/dark_scope/article/details/8880547 趣写算法系列之匈牙利算法

posted @ 2017-09-17 21:44  Wiley_Li  阅读(993)  评论(0编辑  收藏  举报