KM算法的實現和封裝

本身在寫一個小程序時,遇到了一個相似於「完備匹配下的最大權匹配」的優化問題。在網上搜了下相關資料,瞭解到對應的匈牙利算法與KM算法,並且已經都有大神進行了詳細講解和代碼的編寫。惟一的不一樣之處是我參考的文章中KM算法目標是匹配結果最大爲目標,而個人程序中是以匹配結果最小爲目標。本身把代碼改寫了下,並封裝爲類。驗證結果代表代碼沒有問題~html

這裏將代碼放出,以供網友參考。不過具體到算法層,本人理解不深,仍是觀摩網上算法大神的講解吧。ios

代碼以下:算法

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.cppide

  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

參考文章:this

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

http://blog.csdn.net/kevinjqy/article/details/54584114 分配問題與匈牙利算法.net

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

相關文章
相關標籤/搜索