先上代碼
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<cmath>
#include<fstream>
#include<iomanip>
using namespace std;
const int NP = 40;//種群的規(guī)模,采蜜蜂+觀察蜂
const int FoodNumber = NP / 2;//食物的數(shù)量吗跋,為采蜜蜂的數(shù)量
const int limit = 20;//限度侧戴,超過這個(gè)限度沒有更新采蜜蜂變成偵查蜂
const int maxCycle = 10;//停止條件
double result[maxCycle] = { 0 };//每輪循環(huán)中的函數(shù)值
/*****函數(shù)的特定參數(shù)*****/
const int D = 2;//函數(shù)的參數(shù)個(gè)數(shù)
const double lb = -5;//函數(shù)的下界
const double ub = 5;//函數(shù)的上界
/*****種群的定義****///定義了輸入輸出結(jié)構(gòu),IO結(jié)構(gòu)
struct BeeGroup
{
double code[D];//函數(shù)的維數(shù) //估計(jì)是函數(shù)有幾個(gè)參數(shù)小腊,不是幾個(gè)參數(shù)用D就可以定義救鲤。我知道了,是輸入變量的函數(shù)值秩冈。
double trueFit;//記錄真實(shí)的最小值//估計(jì)是最優(yōu)函數(shù)值
double fitness;//把最有函數(shù)值轉(zhuǎn)化為適應(yīng)度
double rfitness;//相對(duì)適應(yīng)值比例
int trail;//表示實(shí)驗(yàn)的次數(shù)本缠,用于與limit作比較//隨機(jī)采樣的次數(shù)。
}Bee[FoodNumber];//FoodNumber表示多少個(gè)采樣點(diǎn)入问。然后再采樣點(diǎn)附近局部采樣丹锹。
BeeGroup NectarSource[FoodNumber];//蜜源,注意:一切的修改都是針對(duì)蜜源而言的 //蜜源應(yīng)該是可行解集合 //芬失?
BeeGroup EmployedBee[FoodNumber];//采蜜蜂 //每個(gè)采樣點(diǎn)都有人在計(jì)算(采蜜/IO)
BeeGroup OnLooker[FoodNumber];//觀察蜂 //楣黍?
BeeGroup BestSource;//記錄最好蜜源 //記錄最有解的input或者output,或者都記錄棱烂∽馄看到initial就是到時(shí)最有xy對(duì)。
/*****函數(shù)的聲明*****/
double random(double, double);//產(chǎn)生區(qū)間上的隨機(jī)數(shù)
void initilize();//初始化參數(shù)
double calculationTruefit(BeeGroup);//計(jì)算真實(shí)的函數(shù)值
double calculationFitness(double);//計(jì)算適應(yīng)值
void CalculateProbabilities();//計(jì)算輪盤賭的概率
void evalueSource();//評(píng)價(jià)蜜源
void sendEmployedBees();//采蜜蜂颊糜,計(jì)算蜂哩治,局部變異蜂
void sendOnlookerBees();//觀察蜂?
void sendScoutBees();//偵察蜂衬鱼?
void MemorizeBestSource();//記錄最有解xy對(duì)的函數(shù)
/*******主函數(shù)*******/
int main()
{
ofstream output; output.open("dataABC.txt");//寫文件
srand((unsigned)time(NULL));//生成隨機(jī)數(shù)业筏,隨機(jī)種子是時(shí)間
initilize();//初始化
MemorizeBestSource();//保存最好的蜜源
int gen = 0;
while (gen < maxCycle) {
sendEmployedBees();//體現(xiàn)計(jì)算蜂功能的時(shí)候到了//ok,計(jì)算蜂的功能是在采樣點(diǎn)附近找更優(yōu)的解鸟赫。//這個(gè)函數(shù)只是進(jìn)行了一次局部搜索
CalculateProbabilities();
sendOnlookerBees();//Onlook的功能是什么蒜胖?也是進(jìn)行一次局部變異
MemorizeBestSource();
sendScoutBees();//局部嘗試若干次,無法找到最優(yōu)解抛蚤,就換個(gè)地方找找
MemorizeBestSource();
output << setprecision(10) << BestSource.trueFit << ","<< BestSource.code[0]<<","<< BestSource.code[1] << endl;
gen++;
}
output.close();
cout << "運(yùn)行結(jié)束!!" << endl;//C++編程語言互換流中的標(biāo)準(zhǔn)輸出流台谢,需要iostream支持。
system("pause");
return 0;
}
void initilize()//初始化參數(shù)
{
int i, j;
for (i = 0; i<FoodNumber; i++) //就是隨機(jī)取多少個(gè)可行解岁经。
{
for (j = 0; j<D; j++)//如果輸入x是向量的話就要用到D对碌,就是一共有幾個(gè)輸入。
{
NectarSource[i].code[j] = random(lb, ub);//cout << NectarSource[i].code[j] << endl;
//code表示輸入x蒿偎,是隨機(jī)生成的可行解朽们。lb, ub是給定了上下界。NectarSource[i]也是xy對(duì)诉位,但為什么取名Nectar骑脱,和其他類似數(shù)組有何區(qū)別不得而知。
EmployedBee[i].code[j] = NectarSource[i].code[j];
//同樣苍糠,計(jì)算蜂也是xy對(duì)叁丧,EmployedBee[i].code[j] = NectarSource[i].code[j];表示蜜源Nectar被計(jì)算了,交給Employ計(jì)算/采蜜了
OnLooker[i].code[j] = NectarSource[i].code[j];
//OnLook的功能岳瞭?
BestSource.code[j] = NectarSource[0].code[j];
//BestSource是用來存放最有xy對(duì)的拥娄。
}
/****蜜源的初始化*****/
NectarSource[i].trueFit = calculationTruefit(NectarSource[i]);//cout << NectarSource[i].trueFit << endl;
NectarSource[i].fitness = calculationFitness(NectarSource[i].trueFit); //cout << NectarSource[i].fitness << endl;
NectarSource[i].rfitness = 0;
NectarSource[i].trail = 0;
/****采蜜蜂的初始化*****/
//采蜜蜂的計(jì)算功能由蜜源承擔(dān)了
EmployedBee[i].trueFit = NectarSource[i].trueFit;
EmployedBee[i].fitness = NectarSource[i].fitness;
EmployedBee[i].rfitness = NectarSource[i].rfitness;
EmployedBee[i].trail = NectarSource[i].trail;
/****觀察蜂的初始化****/
//觀察蜂的功能仍然不清楚
OnLooker[i].trueFit = NectarSource[i].trueFit;
OnLooker[i].fitness = NectarSource[i].fitness;
OnLooker[i].rfitness = NectarSource[i].rfitness;
OnLooker[i].trail = NectarSource[i].trail;
}
/*****最優(yōu)蜜源的初始化*****/
//隨機(jī)選定一個(gè)最有解/初始最優(yōu)解。
BestSource.trueFit = NectarSource[0].trueFit;
BestSource.fitness = NectarSource[0].fitness;
BestSource.rfitness = NectarSource[0].rfitness;
BestSource.trail = NectarSource[0].trail;
}
double random(double start, double end)//隨機(jī)產(chǎn)生區(qū)間內(nèi)的隨機(jī)數(shù)
{
return start + (end - start)*rand() / (RAND_MAX + 1.0);
}
double calculationTruefit(BeeGroup bee)//計(jì)算真實(shí)的函數(shù)值 //外面?zhèn)魅氲拿墼赐ぃ@里傳入的是的形參是bee稚瘾,bee的功能相當(dāng)于一個(gè)橋梁,傳遞參數(shù)姚炕。
{
//計(jì)算一下目標(biāo)函數(shù)值摊欠。
double truefit = 0;
/******測試函數(shù)1******/
truefit = bee.code[0] * bee.code[0] + bee.code[1] * bee.code[1]; //bee.code[0]* bee.code[1]*bee.code[0]* bee.code[1];
return truefit;
}
double calculationFitness(double truefit)//計(jì)算適應(yīng)值 //構(gòu)造了一個(gè)連續(xù)遞減函數(shù)
{
double fitnessResult = 0;
if (truefit >= 0)
{
fitnessResult = 1 / (truefit + 1);
}
else
{
fitnessResult = 1 + abs(truefit);
}
return fitnessResult;
}
//以上4個(gè)函數(shù)共同完成了initial 操作,下面返回主函數(shù)
//主函數(shù)下一步是保存最好蜜源柱宦,就是最有xy解些椒。
void MemorizeBestSource()//保存最優(yōu)的蜜源 //就是把當(dāng)前最優(yōu)解與第i次循環(huán)中所有采蜜點(diǎn)比較
{
int i, j;
for (i = 1; i<FoodNumber; i++)
{
if (NectarSource[i].trueFit<BestSource.trueFit)
{
for (j = 0; j<D; j++)
{
BestSource.code[j] = NectarSource[i].code[j];
}
BestSource.trueFit = NectarSource[i].trueFit;
}
}
}
void sendEmployedBees()//修改采蜜蜂的函數(shù)
{
int i, j, k;
int param2change;//需要改變的維數(shù)
double Rij;//[-1,1]之間的隨機(jī)數(shù)
for (i = 0; i<FoodNumber; i++){//對(duì)于每個(gè)采樣點(diǎn)都隨機(jī)局部變換
//對(duì)每個(gè)采樣點(diǎn)都遍歷一遍
param2change = (int)random(0, D);//隨機(jī)選取需要改變的維數(shù),取向量輸入x的一個(gè)子集掸刊。
/******選取不等于i的k********/
while (1){
k = (int)random(0, FoodNumber);
if (k != i){
break;
}
}
for (j = 0; j<D; j++){
EmployedBee[i].code[j] = NectarSource[i].code[j];
}
/*******采蜜蜂去更新信息*******/
Rij = random(-1, 1);
EmployedBee[i].code[param2change] = (1- Rij)*NectarSource[i].code[param2change] + Rij*(NectarSource[k].code[param2change]);
//隨機(jī)選擇一個(gè)局部點(diǎn)位code[param2change]免糕,在這個(gè)點(diǎn)位上把采樣點(diǎn)i和k的值進(jìn)行雜交
/*******判斷是否越界********/
if (EmployedBee[i].code[param2change]>ub){
EmployedBee[i].code[param2change] = ub;
}
if (EmployedBee[i].code[param2change]<lb){
EmployedBee[i].code[param2change] = lb;
}
EmployedBee[i].trueFit = calculationTruefit(EmployedBee[i]);
EmployedBee[i].fitness = calculationFitness(EmployedBee[i].trueFit);
/******貪婪選擇策略*******/
if (EmployedBee[i].trueFit<NectarSource[i].trueFit){
for (j = 0; j<D; j++){
NectarSource[i].code[j] = EmployedBee[i].code[j];
}
NectarSource[i].trail = 0;
NectarSource[i].trueFit = EmployedBee[i].trueFit;
NectarSource[i].fitness = EmployedBee[i].fitness;
}
else{
NectarSource[i].trail++;
}
}
}
void CalculateProbabilities()//計(jì)算輪盤賭的選擇概率
{
int i;
double maxfit;
maxfit = NectarSource[0].fitness;
for (i = 1; i<FoodNumber; i++)
{
if (NectarSource[i].fitness>maxfit)
maxfit = NectarSource[i].fitness;
}
for (i = 0; i<FoodNumber; i++)
{
NectarSource[i].rfitness = (0.9*(NectarSource[i].fitness / maxfit)) + 0.1;
}
}
void sendOnlookerBees()//采蜜蜂與觀察蜂交流信息,觀察蜂更改信息
{
int i, j, t, k;
double R_choosed;//被選中的概率
int param2change;//需要被改變的維數(shù)
double Rij;//[-1,1]之間的隨機(jī)數(shù)
i = 0;
t = 0;
while (t<FoodNumber){
R_choosed = random(0, 1);
if (R_choosed<NectarSource[i].rfitness){//根據(jù)被選擇的概率選擇 //對(duì)于每個(gè)采樣點(diǎn)忧侧,都在局部變動(dòng)一次石窑,一定會(huì)被選擇,不選擇t就保持不變苍柏,
//不出循環(huán)尼斧,t<FoodNumber,說明t和全局采樣點(diǎn)有關(guān)试吁。i是NectarSource中的一點(diǎn)棺棵,說明也與全局采樣點(diǎn)有關(guān)。
t++;
param2change = (int)random(0, D);//選擇變異的x分量param2change
/******選取不等于i的k********/
while (1){
k = (int)random(0, FoodNumber);
if (k != i){
break;
}
}
for (j = 0; j<D; j++){
OnLooker[i].code[j] = NectarSource[i].code[j];
}
/****更新******///完成對(duì)param2change的變異熄捍,i=t-1
Rij = random(-1, 1);
OnLooker[i].code[param2change] =(1-Rij)*NectarSource[i].code[param2change] + Rij*(NectarSource[k].code[param2change]);
/*******判斷是否越界*******/
if (OnLooker[i].code[param2change]<lb){
OnLooker[i].code[param2change] = lb;
}
if (OnLooker[i].code[param2change]>ub){
OnLooker[i].code[param2change] = ub;
}
OnLooker[i].trueFit = calculationTruefit(OnLooker[i]);
OnLooker[i].fitness = calculationFitness(OnLooker[i].trueFit);
/****貪婪選擇策略******/
if (OnLooker[i].trueFit<NectarSource[i].trueFit){
for (j = 0; j<D; j++)
{
NectarSource[i].code[j] = OnLooker[i].code[j];
}
NectarSource[i].trail = 0;
NectarSource[i].trueFit = OnLooker[i].trueFit;
NectarSource[i].fitness = OnLooker[i].fitness;
}
else
{
NectarSource[i].trail++;
}
}
i++;
if (i == FoodNumber)
{
i = 0;
}
}
}
/*******只有一只偵查蜂**********/
void sendScoutBees()//判斷是否有偵查蜂的出現(xiàn)烛恤,有則重新生成蜜源
{
int maxtrialindex, i, j;
double R;//[0,1]之間的隨機(jī)數(shù)
maxtrialindex = 0;
for (i = 1; i<FoodNumber; i++)
{
if (NectarSource[i].trail>NectarSource[maxtrialindex].trail)
{
maxtrialindex = i;
}
}
if (NectarSource[maxtrialindex].trail >= limit)
{
/*******重新初始化*********/
for (j = 0; j<D; j++)
{
R = random(0, 1);
NectarSource[maxtrialindex].code[j] = lb + R*(ub - lb);
}
NectarSource[maxtrialindex].trail = 0;
NectarSource[maxtrialindex].trueFit = calculationTruefit(NectarSource[maxtrialindex]);
NectarSource[maxtrialindex].fitness = calculationFitness(NectarSource[maxtrialindex].trueFit);
}
}
這段代碼解決的問題是x12+x22,迭代10次余耽,結(jié)果可以缚柏,如圖所示。
還有一個(gè)問題碟贾,Onlooker和Employed的區(qū)別是什么币喧?
EmployedBees:將采蜜蜂與蜜源一一對(duì)應(yīng)轨域,根據(jù)上面第一個(gè)公式更新蜜源信息,同時(shí)確定蜜源的花蜜量杀餐;對(duì)于每個(gè)采礦點(diǎn)干发,先進(jìn)行一次試探性的局部搜索。
OnlookerBees:根據(jù)試探的結(jié)果史翘,有選擇性的進(jìn)一步挖掘枉长。挖掘就是多試幾次。多產(chǎn)生幾次隨機(jī)數(shù)琼讽。利用輪盤賭概率必峰,選中某個(gè)采礦點(diǎn),然后在其附近搜索钻蹬。
ScoutBees:當(dāng)所有的采蜜蜂和觀察蜂都搜索完整個(gè)搜索空間時(shí)吼蚁,如果一個(gè)蜜源的適應(yīng)值在給定的步驟內(nèi)(定義為控制參數(shù)“l(fā)imit”) 沒有被提高, 則丟棄該蜜源,而與該蜜源相對(duì)應(yīng)的采蜜蜂變成偵查蜂脉让,偵查蜂通過已下公式搜索新的可能解桂敛。意思就是說生產(chǎn)全局最優(yōu)解。
俄羅斯輪盤賭的選擇情況如下圖溅潜,但是OnlookerBees不是這么選的术唬。t<=FoodNumber,就是說選中20次滚澜,至于選哪個(gè)粗仓,從0個(gè)開礦點(diǎn)開始,沒被選中设捐,就換下一個(gè)采礦點(diǎn)借浊。選中了t=t+1;然后再對(duì)第i個(gè)采礦點(diǎn)進(jìn)行局部變異搜索萝招。那么ABC里的選擇方法比輪盤賭又高明了一點(diǎn)蚂斤。
case 2
經(jīng)過多次對(duì)照,確實(shí)可以求到近似最優(yōu)解槐沼。
【-1曙蒸,5】,最小值在5附近岗钩,ABC算法求導(dǎo)的最優(yōu)解確實(shí)4.9x纽窟,沒錯(cuò)。