7 ABC算法 的概念,代碼,小例子

先上代碼

#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;//限度,超過(guò)這個(gè)限度沒(méi)有更新采蜜蜂變成偵查蜂  
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,或者都記錄??吹絠nitial就是到時(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();//局部嘗試若干次,無(wú)法找到最優(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++編程語(yǔ)言互換流中的標(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是用來(lái)存放最有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;
    /******測(cè)試函數(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,說(shuō)明t和全局采樣點(diǎn)有關(guān)。i是NectarSource中的一點(diǎn),說(shuō)明也與全局采樣點(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);
    }
}

這段代碼解決的問(wèn)題是x12+x22,迭代10次,結(jié)果可以,如圖所示。

收斂趨勢(shì)

還有一個(gè)問(wèn)題,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”) 沒(méi)有被提高, 則丟棄該蜜源,而與該蜜源相對(duì)應(yīng)的采蜜蜂變成偵查蜂,偵查蜂通過(guò)已下公式搜索新的可能解。意思就是說(shuō)生產(chǎn)全局最優(yōu)解。

俄羅斯輪盤賭的選擇情況如下圖,但是OnlookerBees不是這么選的。t<=FoodNumber,就是說(shuō)選中20次,至于選哪個(gè),從0個(gè)開(kāi)礦點(diǎn)開(kāi)始,沒(méi)被選中,就換下一個(gè)采礦點(diǎn)。選中了t=t+1;然后再對(duì)第i個(gè)采礦點(diǎn)進(jìn)行局部變異搜索。那么ABC里的選擇方法比輪盤賭又高明了一點(diǎn)。


Paste_Image.png

case 2


Paste_Image.png

Paste_Image.png

經(jīng)過(guò)多次對(duì)照,確實(shí)可以求到近似最優(yōu)解。
【-1,5】,最小值在5附近,ABC算法求導(dǎo)的最優(yōu)解確實(shí)4.9x,沒(méi)錯(cuò)。


Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容