本身在寫一個小程序時,遇到了一個相似於「完備匹配下的最大權匹配」的優化問題。在網上搜了下相關資料,瞭解到對應的匈牙利算法與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
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 }
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 }
參考文章: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