人工蜂群演算法簡介與程式分析
目前人工蜂群演算法主要分為基於婚配行為與基於釆蜜行為兩大類,本文研究的是基於釆蜜行為的人工蜂群演算法。
蜜蜂採蜜
自然界中的蜜蜂總能在任何環境下以極高的效率找到優質蜜源,且能適應環境的改變。蜜蜂群的採蜜系統由蜜源、僱傭蜂、非僱傭蜂三部分組成,其中一個蜜源的優劣有很多要素,如蜜源花蜜量的大小、離蜂巢距離的遠近、提取的難易程度等;僱傭蜂和特定的蜜源聯絡並將蜜源資訊以一定概率形式告訴同伴;非僱傭蜂的職責是尋找待開採的蜜源,分為跟隨蜂和偵查蜂兩類,跟隨峰是在蜂巢等待而偵查蜂是探測蜂巢周圍的新蜜源。蜜蜂採蜜時,蜂巢中的一部分蜜蜂作為偵查蜂,不斷並隨機地在蜂巢附近尋找蜜源,如果發現了花蜜量超過某個閾值的蜜源,則此偵査蜂變為僱傭蜂開始釆蜜,採蜜完成後飛回蜂巢跳搖擺舞告知跟隨峰。搖擺舞是蜜蜂之間交流資訊的一種基本形式,它傳達了有關蜂巢周圍蜜源的重要資訊如蜜源方向及離巢距離等,跟隨峰利用這些資訊準確評價蜂巢周圍的蜜源質量。當僱傭蜂跳完搖擺舞之後,就與蜂巢中的一些跟隨蜂一起返回原蜜源採蜜,跟隨蜂數量取決於蜜源質量。以這種方式,蜂群能快速且有效地找到花蜜量最高的蜜源。
蜜蜂採蜜的群體智慧就是通過不同角色之間的交流轉換及協作來實現的。具體採蜜過程如圖所示。在最初階段,蜜蜂是以偵查蜂的形式出現,且對蜂巢周闈的蜜源沒有任何瞭解,由於蜜蜂內在動機和外在的條件不同偵查蜂有兩種選擇:①成為僱傭蜂,開始在蜂巢周圍隨機搜尋蜜源,如圖中路線②成為跟隨峰,在觀察完搖擺舞後開始搜尋蜜源,如圖中路線。假設發現兩個蜜源和,在發現蜜源後,該偵查蜂變成一隻僱傭蜂,僱傭蜂利用其自身屬性記住蜜源的位置,並立刻投入到採蜜中。採蜜完成後蜜蜂帶著滿載花蜜返回蜂巢,將花蜜解除安裝到卸蜜房,解除安裝完成後僱傭蜂有三種可能的行為①放棄自己發現的花蜜量不高的蜜源,變成一個不受約束的非僱傭蜂,如圖中的路線;②在
招募區跳搖擺舞,招募一些待在蜂巢中跟隨峰,帶領其再次返回所發現的蜜源如圖中的路線;③不招募其他蜜蜂,繼續回到原來的蜜源採蜜如圖中的路線。在現實生活中並不是所有的蜜蜂一開始就立刻採蜜,另外大多數蜜蜂在一次採蜜完成後都會選擇回到招募區跳搖擺舞來招募更多的蜜蜂去採蜜。
演算法模型
人工蜂群演算法就是模擬蜜蜂的採蜜過程而提出的一種新型智慧優化演算法,它也是由食物源、僱傭蜂和非僱傭蜂三部分組成。
食物源:食物源即為蜜源。在任何一個優化問題中,問題的可行解都是以一定形式給出的。在人工蜂群演算法中,食物源就是待求優化問題的可行解,是人工蜂群演算法中所要處理的基本物件。食物源的優劣即可行解的好壞是用蜜源花蜜量的大小即適應度來評價的。
僱傭蜂:僱傭蜂即為引領蜂與食物源的位置相對應,一個食物源對應一個引領蜂。在人工蜂群演算法中,食物源的個數與引領蜂的個數相等;引領蜂的任務是發現食物源資訊並以一定的概率與跟隨蜂分享;概率的計算即為人工蜂群演算法中的選擇策略,一般是根據適應度值以輪盤賭的方法計算。
非僱傭蜂:非僱傭蜂包括跟隨蜂和偵査蜂跟隨蜂在蜂巢的招募區內根據引領蜂提供的蜜源資訊來選擇食物源,而偵查蜂是在蜂巢附近尋找新的食物源。在人工蜂群演算法中,跟隨蜂依據引領蜂傳遞的資訊,在食物源附近搜尋新食物源,並進行貪婪選擇。若一個食物源在經過次後仍未被更新,則此引領蜂變成偵査蜂,偵查蜂尋找新的食物源代替原來的食物源。
演算法搜尋過程
人工蜂群演算法中將人工蜂群分為引領蜂、跟隨蜂和偵查蜂三類,每一次搜尋過程中,引領蜂和跟隨蜂是先後開採食物源,即尋找最優解,而偵查蜂是觀察是否陷入區域性最優,若陷入區域性最優則隨機地搜尋其它可能的食物源。每個食物源代表問題一個可能解,食物源的花蜜量對應相應解的質量(適應度值fiti)。
一、人工蜂群演算法搜尋過程中,首先需要初始化,其中包括確定種群數、最大迭代次數MCN、、控制引數limit和確定搜尋空間即解的範圍,在搜尋空間中隨機生成初始解xi(i=1,2,3,……,SN),SN為食物源個數,每個解xi是一個D維的向量,D是問題的維數。初始化之後,整個種群將進行引領蜂、跟隨蜂和偵查蜂搜尋過程的重複迴圈,直到達到最大迭代次數MCN或誤差允許值 ε。
二、在搜尋過程開始階段,每個引領蜂由式(2-3)產生一個新解即新食物源,
vij=xij+Φij(xij-xkj) (2-3)
式中,k∈﹛1,2,...,SN﹜,j∈{1,2,...,D},且k ≠i;Φij為[-1,1]之間的隨機數。計算新解的fiti並評價它,若新解的fiti優於舊解,則引領蜂記住新解忘記舊解。反之,它將保留舊解。
三、在所有引領蜂完成搜尋過程之後,引領蜂會在招募區跳搖擺舞把解的資訊及資訊與跟隨蜂分享。跟隨蜂根據式計算每個解的選擇概率,
pi=fiti/∑k=1SNfitk。 (2-4)
然後在區間[-1,1]內隨機產生一個數,如果解的概率值大於該隨機數,則跟隨蜂由式(2-3)產生一個新解,並檢驗新解的fiti,若新解的fiti比之前好,則跟隨蜂將記住新解忘掉舊解;反之,它將保留舊解。
四、在所有跟隨蜂完成搜尋過程之後,如果一個解經過limit次迴圈仍然沒有被進一步更新,那麼就認為此解陷入區域性最優,該食物源就會被捨棄。設食物源xi被捨棄,則此食物源對應的引領蜂轉成一個偵查蜂。偵察蜂由(2-5)式產生一個新的食物源代替它。
xij=xminj+rand(0,1)(xmaxj-xminj) (2-5)
其中j∈{1,2....,D}。然後返回引領蜂搜尋過程,開始重複迴圈。
五、人工蜂群演算法的食物源質量一般是越大越好,即適應度值越大越好,而對應於要優化的問題,需要分兩種情況考慮:即最小值問題、最大值問題。設fi是優化問題的目標函式,所以若優化最小值問題時,適應度函式為fi的變形,一般用式(2-6)表示;若優化最大值問題,適應度函式即目標函式。
fiti={1+abs(fi) fi>=01/1+fi fi>0 (2-6)
人工蜂群演算法在評價食物源時一般進行貪婪選擇按式(2-7)進行。
vi={xi fit(xi)<=fit(vi)vi fit(vi)>fit(xi) (2-7)
人工蜂群演算法就是通過迴圈搜尋,最終找到最優食物源或最優解。
演算法步驟
人工蜂群演算法具體實現步驟:
步驟1:初始化種群:初始化各個引數,蜂群總數SN、食物源被採集次數即最大迭代次數MCN及控制引數limit,確定問題搜尋範圍,並且在搜尋範圍內隨機產生初始解xi(i=1,2,...SN) 。
步驟2:計算並評估每個初始解的適應度。
步驟3:設定迴圈條件並開始迴圈
步驟4:引領蜂對解xi按照式(2-3)進行鄰域搜尋產生新解(食物源)vi,並計算其適應度值;
步驟5:按照式(2-7)進行貪婪選擇:如果vi的適應度值優於xi,則利用vi替換xi,將vi作為當前最好的解,否則保留xi不變;
步驟6:根據式(2-4)計算食物源的概率pi;
步驟7:跟隨蜂依照概率pi選擇解或食物源,按照式(2-3)搜尋產生新解(食物源)vi,並計算其適應度。
程式分析
程式中的採蜜蜂就是引領蜂也是僱傭蜂,都表達一個含義。
1 #include<iostream> 2 #include<time.h> 3 #include<stdlib.h> 4 #include<cmath> 5 #include<fstream> 6 #include<iomanip> 7 using namespace std; 8 9 const int NP=40;//種群的規模,採蜜蜂+觀察蜂 10 const int FoodNumber=NP/2;//食物的數量,為採蜜蜂的數量 11 const int limit=20;//限度,超過這個限度沒有更新採蜜蜂變成偵查蜂 12 const int maxCycle=10000;//停止條件 13 14 /*****函式的特定引數*****/ 15 const int D=2;//函式的引數個數 16 const double lb=-100;//函式的下界 17 const double ub=100;//函式的上界 18 19 double result[maxCycle]={0}; 20 21 /*****種群的定義****/ 22 struct BeeGroup 23 { 24 double code[D];//函式的維數 25 double trueFit;//記錄真實的最小值 26 double fitness; 27 double rfitness;//相對適應值比例 28 int trail;//表示實驗的次數,用於與limit作比較 29 }Bee[FoodNumber]; 30 31 BeeGroup NectarSource[FoodNumber];//蜜源,注意:一切的修改都是針對蜜源而言的 32 BeeGroup EmployedBee[FoodNumber];//採蜜蜂 33 BeeGroup OnLooker[FoodNumber];//觀察蜂 34 BeeGroup BestSource;//記錄最好蜜源 35 36 /*****函式的宣告*****/ 37 double random(double, double);//產生區間上的隨機數 38 void initilize();//初始化引數 39 double calculationTruefit(BeeGroup);//計算真實的函式值 40 double calculationFitness(double);//計算適應值 41 void CalculateProbabilities();//計算輪盤賭的概率 42 void evalueSource();//評價蜜源 43 void sendEmployedBees(); 44 void sendOnlookerBees(); 45 void sendScoutBees(); 46 void MemorizeBestSource(); 47 48 49 /*******主函式*******/ 50 int main() 51 { 52 ofstream output; //輸出定義 53 output.open("dataABC.txt"); 54 55 srand((unsigned)time(NULL)); //根據時間產生隨機種子 56 initilize();//初始化 57 MemorizeBestSource();//儲存最好的蜜源 58 59 //主要的迴圈 60 int gen=0; 61 while(gen<maxCycle) 62 { 63 sendEmployedBees(); 64 65 CalculateProbabilities(); 66 67 sendOnlookerBees(); 68 69 MemorizeBestSource(); 70 71 sendScoutBees(); 72 73 MemorizeBestSource(); 74 75 output<<setprecision(30)<<BestSource.trueFit<<endl; //輸出30個有效數字 76 77 gen++; 78 } 79 80 output.close(); 81 cout<<"執行結束!!"<<endl; 82 return 0; 83 } 84 85 /*****函式的實現****/ 86 double random(double start, double end)//隨機產生區間內的隨機數 87 { 88 return start+(end-start)*rand()/(RAND_MAX + 1.0); 89 } 90 91 void initilize()//初始化引數 92 { 93 int i,j; 94 for (i=0;i<FoodNumber;i++) 95 { 96 for (j=0;j<D;j++) 97 { 98 NectarSource[i].code[j]=random(lb,ub); 99 EmployedBee[i].code[j]=NectarSource[i].code[j]; 100 OnLooker[i].code[j]=NectarSource[i].code[j]; 101 BestSource.code[j]=NectarSource[0].code[j]; 102 } 103 /****蜜源的初始化*****/ 104 NectarSource[i].trueFit=calculationTruefit(NectarSource[i]); 105 NectarSource[i].fitness=calculationFitness(NectarSource[i].trueFit); 106 NectarSource[i].rfitness=0; 107 NectarSource[i].trail=0; 108 /****採蜜蜂的初始化*****/ 109 EmployedBee[i].trueFit=NectarSource[i].trueFit; 110 EmployedBee[i].fitness=NectarSource[i].fitness; 111 EmployedBee[i].rfitness=NectarSource[i].rfitness; 112 EmployedBee[i].trail=NectarSource[i].trail; 113 /****觀察蜂的初始化****/ 114 OnLooker[i].trueFit=NectarSource[i].trueFit; 115 OnLooker[i].fitness=NectarSource[i].fitness; 116 OnLooker[i].rfitness=NectarSource[i].rfitness; 117 OnLooker[i].trail=NectarSource[i].trail; 118 } 119 /*****最優蜜源的初始化*****/ 120 BestSource.trueFit=NectarSource[0].trueFit; 121 BestSource.fitness=NectarSource[0].fitness; 122 BestSource.rfitness=NectarSource[0].rfitness; 123 BestSource.trail=NectarSource[0].trail; 124 } 125 126 double calculationTruefit(BeeGroup bee)//計算真實的函式值 127 { 128 double truefit=0; 129 /******測試函式1******/ 130 truefit=0.5+(sin(sqrt(bee.code[0]*bee.code[0]+bee.code[1]*bee.code[1]))*sin(sqrt(bee.code[0]*bee.code[0]+bee.code[1]*bee.code[1]))-0.5) 131 /((1+0.001*(bee.code[0]*bee.code[0]+bee.code[1]*bee.code[1]))*(1+0.001*(bee.code[0]*bee.code[0]+bee.code[1]*bee.code[1]))); 132 133 return truefit; 134 } 135 136 double calculationFitness(double truefit)//計算適應值 137 { 138 double fitnessResult=0; 139 if (truefit>=0) 140 { 141 fitnessResult=1/(truefit+1); 142 }else 143 { 144 fitnessResult=1+abs(truefit); 145 } 146 return fitnessResult; 147 } 148 149 void sendEmployedBees()//修改採蜜蜂的函式 150 { 151 int i,j,k; 152 int param2change;//需要改變的維數 153 double Rij;//[-1,1]之間的隨機數 154 for (i=0;i<FoodNumber;i++) 155 { 156 157 param2change=(int)random(0,D);//隨機選取需要改變的維數 158 159 /******選取不等於i的k********/ 160 while (1) 161 { 162 k=(int)random(0,FoodNumber); 163 if (k!=i) 164 { 165 break; 166 } 167 } 168 169 for (j=0;j<D;j++) 170 { 171 EmployedBee[i].code[j]=NectarSource[i].code[j]; //在之前初始化對EmployedBee進行初始化了,程式之後有對蜜源改變在此對EmployedBee進行更新 172 } 173 174 /*******採蜜蜂去更新資訊*******/ 175 Rij=random(-1,1); 176 EmployedBee[i].code[param2change]=NectarSource[i].code[param2change]+Rij*(NectarSource[i].code[param2change]-NectarSource[k].code[param2change]); //根據公式(2-3) 177 /*******判斷是否越界********/ 178 if (EmployedBee[i].code[param2change]>ub) 179 { 180 EmployedBee[i].code[param2change]=ub; 181 } 182 if (EmployedBee[i].code[param2change]<lb) 183 { 184 EmployedBee[i].code[param2change]=lb; 185 } 186 EmployedBee[i].trueFit=calculationTruefit(EmployedBee[i]); 187 EmployedBee[i].fitness=calculationFitness(EmployedBee[i].trueFit); 188 189 /******貪婪選擇策略*******/ 190 if (EmployedBee[i].trueFit<NectarSource[i].trueFit) 191 { 192 for (j=0;j<D;j++) 193 { 194 NectarSource[i].code[j]=EmployedBee[i].code[j]; 195 } 196 NectarSource[i].trail=0; 197 NectarSource[i].trueFit=EmployedBee[i].trueFit; 198 NectarSource[i].fitness=EmployedBee[i].fitness; 199 }else 200 { 201 NectarSource[i].trail++; 202 } 203 } 204 } 205 206 void CalculateProbabilities()//計算輪盤賭的選擇概率 (計算的適應度比例)與後面 sendOnlookerBees中的選擇R_choosed進行比較 207 { 208 int i; 209 double maxfit; 210 maxfit=NectarSource[0].fitness; 211 for (i=1;i<FoodNumber;i++) 212 { 213 if (NectarSource[i].fitness>maxfit) 214 maxfit=NectarSource[i].fitness; 215 } 216 217 for (i=0;i<FoodNumber;i++) 218 { 219 NectarSource[i].rfitness=(0.9*(NectarSource[i].fitness/maxfit))+0.1; 220 } 221 } 222 223 void sendOnlookerBees()//採蜜蜂與觀察蜂交流資訊,觀察蜂更改資訊 224 { 225 int i,j,t,k; 226 double R_choosed;//被選中的概率 227 int param2change;//需要被改變的維數 228 double Rij;//[-1,1]之間的隨機數 229 i=0; 230 t=0; //是否超出食物源個數 231 while(t<FoodNumber) 232 { 233 234 R_choosed=random(0,1); 235 if(R_choosed<NectarSource[i].rfitness)//根據被選擇的概率選擇 (演算法搜尋過程三的實現) 236 { 237 t++; 238 param2change=(int)random(0,D); 239 240 /******選取不等於i的k********/ 241 while (1) 242 { 243 k=(int)random(0,FoodNumber); 244 if (k!=i) 245 { 246 break; 247 } 248 } 249 250 for(j=0;j<D;j++) 251 { 252 OnLooker[i].code[j]=NectarSource[i].code[j]; 253 } 254 255 /****更新******/ 256 Rij=random(-1,1); 257 OnLooker[i].code[param2change]=NectarSource[i].code[param2change]+Rij*(NectarSource[i].code[param2change]-NectarSource[k].code[param2change]); 258 259 /*******判斷是否越界*******/ 260 if (OnLooker[i].code[param2change]<lb) 261 { 262 OnLooker[i].code[param2change]=lb; 263 } 264 if (OnLooker[i].code[param2change]>ub) 265 { 266 OnLooker[i].code[param2change]=ub; 267 } 268 OnLooker[i].trueFit=calculationTruefit(OnLooker[i]); 269 OnLooker[i].fitness=calculationFitness(OnLooker[i].trueFit); 270 271 /****貪婪選擇策略******/ 272 if (OnLooker[i].trueFit<NectarSource[i].trueFit) 273 { 274 for (j=0;j<D;j++) 275 { 276 NectarSource[i].code[j]=OnLooker[i].code[j]; 277 } 278 NectarSource[i].trail=0; 279 NectarSource[i].trueFit=OnLooker[i].trueFit; 280 NectarSource[i].fitness=OnLooker[i].fitness; 281 }else 282 { 283 NectarSource[i].trail++; 284 } 285 } 286 i++; 287 if (i==FoodNumber) 288 { 289 i=0; 290 } 291 } 292 } 293 294 295 /*******只有一隻偵查蜂(主程式進行一次迴圈該函式就判斷一次偵查蜂是否出現)(如果有兩個及以上超過limit限制,該函式能將他們都找到嗎?在迴圈結束之前)**********/ 296 void sendScoutBees()//判斷是否有偵查蜂的出現,有則重新生成蜜源 297 { 298 int maxtrialindex,i,j; 299 double R;//[0,1]之間的隨機數 300 maxtrialindex=0; 301 for (i=1;i<FoodNumber;i++) 302 { 303 if (NectarSource[i].trail>NectarSource[maxtrialindex].trail) 304 { 305 maxtrialindex=i; 306 } 307 } 308 if(NectarSource[maxtrialindex].trail>=limit) 309 { 310 /*******重新初始化*********/ 311 for (j=0;j<D;j++) 312 { 313 R=random(0,1); 314 NectarSource[maxtrialindex].code[j]=lb+R*(ub-lb); //此處蜜源進行更新(這就是為什麼在初始化引領蜂和偵查蜂之後,還要在相應的函式地方再初始化一次) 315 } 316 NectarSource[maxtrialindex].trail=0; 317 NectarSource[maxtrialindex].trueFit=calculationTruefit(NectarSource[maxtrialindex]); 318 NectarSource[maxtrialindex].fitness=calculationFitness(NectarSource[maxtrialindex].trueFit); 319 } 320 } 321 322 void MemorizeBestSource()//儲存最優的蜜源 323 { 324 int i,j; 325 for (i=1;i<FoodNumber;i++) 326 { 327 if (NectarSource[i].trueFit<BestSource.trueFit) 328 { 329 for (j=0;j<D;j++) 330 { 331 BestSource.code[j]=NectarSource[i].code[j]; 332 } 333 BestSource.trueFit=NectarSource[i].trueFit; 334 } 335 } 336 }
另附實驗生成資料資料以及演算法原始碼以供參考 https://pan.baidu.com/s/1iIPfQUSpJHp7_pjJ1SSBEg
請大家斧正
2019-08-17 10:22:31 &n