智能優化算法——布穀鳥搜索算法原理(附代碼)

目錄

基本概念

算法具體流程

         算法流程圖

測試函數

優化結果

visual studio2017C++代碼


基本概念

布穀鳥搜索算法(Cuckoo Search,縮寫 CS)是由劍橋大學楊新社教授和S.戴佈於2009年提出的一種新興啓發算法。根據昆蟲學家的長期觀察研究發現,一部分佈谷鳥以寄生的方式養育幼鳥,它們不築巢,而是將自己的卵產在其他鳥的巢中(通常爲黃鶯、雲雀等),由其他鳥(義親)代爲孵化和育雛。然而,如果這些外來鳥蛋被宿主發現,宿主便會拋棄這些鳥蛋或新築鳥巢。

通俗理解就是,布穀鳥蛋找到能成功在其他鳥巢成功孵化這個過程就是尋優過程。布穀鳥算法源於對布穀鳥繁育行爲的模擬,爲了簡化自然界中布穀鳥的繁衍習性,Yang 等將布穀鳥的產卵行爲假設爲3個理想狀態。

  1. 布穀鳥一次只產一個卵,並隨機選擇鳥窩位置來孵化它。
  2. 在隨機選擇的一組鳥窩中,最好的鳥窩將會被保留到下一代。
  3. 可選擇的寄生巢的數量固定的,寄生巢主人發現外來鳥蛋的概率爲pa,其中 0\leq pa\leqslant 1

基於這 3 個理想狀態,Yang 等採用式(1)對下代鳥巢位置X_{i}^{t+1}進行更新

                                      X_{i}^{t+1}=X_{i}^{t}+\alpha \bigotimes Levy(\lambda )                                           (1)

式中: X_{i}^{t}表示第i(i=1,2,3,...,n)個鳥巢在第t代的位置;\bigotimes 表示點對點乘法; \alpha表示步長控制量,用來控制步長大小,通常情況下,取\alpha=1Levy(\lambda )爲Levy隨機搜索路徑,屬於隨機行走,採用萊維飛行機制,其行走的步長滿足一個重尾的穩定分佈,而隨機步長爲levy分佈:

                                     Levy:\mu =t^{-\lambda },1\leq \lambda \leqslant 3                                               (2)

基本布穀鳥搜索算法先按照式(1)對下一代的鳥巢位置進行更新,並且計算目標函數的適應度值,如果該值優於上一代的目標函數值,則更新鳥巢位置,否則保持原來位置不變。通過位置更新後,用隨機產生的服從 0 到 1 均勻分佈的數值𝑅與鳥巢主人發現外來鳥蛋的概率pa相比較,若R>pa,則X_{i}^{t+1}對進行隨機改變,反之不變。最後保留測試值較好的一組鳥窩位置 X_{i}^{t+1},記爲X_{i}^{t+1} 。判斷算法是否滿足設置的最大迭代次數:若滿足,結束迭代尋優,輸出全局最優值fmin  ;否則,繼續迭代尋優。該算法具有全局探索局部開發性能的平衡以及種羣的多樣性

算法具體流程

Step 1(初始化)確定目標函數f(x),X=(x_{1},...,x_{d})^{T}初始化羣體,隨機產生n個鳥窩的初始位置X_{i}(i=1,2,...,n)設置算法參數:種羣規模N、維度D、發現概率pa、界值大小L、最大迭代次數MaxN、最優鳥窩位置X_{best}^{0}和最優解f_{min} 。

Step 2(循環體)按式(1)、(2)更新當代鳥窩的位置;將當代鳥窩與上一代鳥窩位置P_{t-1}=\left [ X_{1}^{t-1} ,X_{2}^{t-1},...,X_{n}^{t-1} \right ]^{T}進行對比,用適應度值較好的鳥窩位置替換適應度值較差的鳥窩位置:g_{t}=\left [ X_{1}^{t} ,X_{2}^{t},...,X_{n}^{t} \right ]^{T}

Step 3(循環體)用隨機數𝑅作爲鳥窩主人發現外來鳥蛋的可能性,將其與鳥被淘汰的概率pa進行比較。若𝑅 >pa則隨機改變 g_{t}中的鳥窩位置,得到一組新的鳥窩位置。再更新鳥窩位置,得到一組較好的鳥窩位置:p_{t}=\left [ X_{1}^{t} ,X_{2}^{t},...,X_{n}^{t} \right ]^{T}。更新最優鳥窩位置X_{best}^{t}和最優解f^{t}_{min}

Step 4 判斷算法是否滿足設置的最大迭代次數:若滿足,結束搜索過程,輸出全局最優值f_{min},否則,重複step2進行迭代尋優

算法流程圖

測試函數

優化變量:x_{1},x_{2}

約束條件:x_{low}=\left \{ -2,-2 \right \},x_{high}=\left \{ 2,2 \right \}

目標函數:f(x)=\left [ 1+(x_{1}+x_{2}+1)^{2} (19-14x_{1}+3x_{1}^{2}-14x_{2}+6x_{1}x_{2}+3x_{2}^{2})\right ]\times \left [ 30+(2x_{1}-3x_{2})^{2} (18-32x_{1}+12x_{1}^{2}+48x_{2}-36x_{1}x_{2}+27x_{2}^{2})\right ]

式中, Goldstein-Price函數f(x)是一個二元八次多項式,作爲常見的算法測試函數,許多科研人員利用它研究其局部最小值。上式爲該優化問題的數學優化模型,該函數的理論結果爲在(0,-1)處取得最小值3。

優化結果

 從上圖可以看出,布穀鳥搜索算法優化最小值爲3.02573,迭代400次用時2.119s。

visual studio2017C++代碼

頭文件:

//布穀鳥搜索算法
//開發人員:chenshuai      開發日期:2019.11.25  郵箱:[email protected] 
//************************************************************************************//
//布穀鳥算法參數設定
//************************************************************************************//
#ifndef PCH_H
#define PCH_H
#include <iostream>
# include <fstream>
#include <iomanip>
#include <math.h>
#include <cstdlib> // 標準庫
#include <ctime> // 時間庫
#include <vector> //容器頭文件
#include<random>
#define  PI    3.1415926535897    //π值
using namespace std;
//產生隨機小數或整數
class RandomNumber {
public:
	RandomNumber() {
		srand((unsigned)time(NULL));    //析構函數,在對象創建時數據成員執行初始化操作
	}
	int integer(int begin, int end)
	{
		return rand() % (end - begin + 1) + begin;
	}
	double decimal(double a, double b)
	{
		return double(rand() % 10000) / 10000 * (b - a) + a;
	}
};
class cs {
private:
	int N_nest;//種羣(布穀鳥巢總數)
	int Max_iteration;//最大迭代次數,
	int D_egg;//變量個數(布穀鳥蛋維數)
	double pa;//布穀鳥蛋被發現的概率
	double cs_alpha;//步長控制量
	double R;  //隨機數R
public:
	double lambda;
	vector<double>fmin;                      //最優解
	vector<vector<double>>f_nest;            //鳥窩的適應度值
	vector<vector<double>>p_t;              //與上一代比較後最優的鳥窩種羣
	vector<vector<double>>g_t;              //被發現之後,更新的最優鳥窩種羣
	vector<double>nest_best;        //最好的鳥窩
	vector<double>nest_low = { -2,-2 };   //儲存變量上限值  (鳥窩的上限)
	vector<double>nest_high = { 2,2 };   //儲存變量下限值   (鳥窩的下限)
	//*******************************************//
	//定義函數
	//*******************************************//
	void setParameters();//設置算法參數int N_nest,int Max_iteration,int D_egg,double cs_alpha
	void initialize();                //初始化
	double select_optimal(vector<vector<double>>x);            //選擇最優的鳥巢
	double fit_function(vector<double> x); //目標函數
	vector<vector<double>> update_Levyflight(vector<vector<double>>x, int t);//萊維飛行更新鳥窩位置
	vector<vector<double>> update_Rrandomnumber(vector<vector<double>>x);//隨機數R更新鳥窩位置
};

#endif //PCH_H

函數文件:

#include "pch.h"
//***********************************************
//設置布穀鳥算法參數
//**********************************************
void cs::setParameters()
{
	N_nest = 100; //種羣(布穀鳥巢總數)
	Max_iteration = 400;//最大迭代次數,
	D_egg = 2;//變量個數(布穀鳥蛋維數)
	pa = 0.25;//布穀鳥蛋被發現的概率
	cs_alpha = 1.0;//步長控制量
}
//**********************************************
//初始化羣體,N個鳥窩的初始位置
//**********************************************
void cs::initialize()
{
	extern RandomNumber r;       //定義隨機數
	fmin.resize(Max_iteration);
	p_t.resize(N_nest, vector<double>(D_egg));
	g_t.resize(N_nest, vector<double>(D_egg));
	f_nest.resize(Max_iteration, vector<double>(N_nest));
	nest_best.resize(D_egg);
	for (int i = 0; i < N_nest; i++)
	{
		for (int j = 0; j < D_egg; j++)
		{
			p_t[i][j] = r.decimal(nest_low[j], nest_high[j]);//初始化鳥窩的值

		}
	}
}
//***********************************************
//鳥窩位置的適應度
//**********************************************
double cs::fit_function(vector<double>x)
{
	double fx = 0;
	//Rastrigin函數
	/*for (int i = 0; i < 2; i++)
	{
		fx = fx + x[i] * x[i] - 10 * cos(2 * PI*x[i]) + 10;
	}*/
	//Goldstein	-Price函數
	fx = (1 + pow((1 + x[0] + x[1]), 2)*(19 - 14 * x[0] + 3 * x[0] * x[0] - 14 * x[1] + 6 * x[0] * x[1] + 3 * x[1] * x[1]))*(30 + pow((2 * x[0] - 3 * x[1]), 2)*(18 - 32 * x[0] + 12 * x[0] * x[0] + 48 * x[1] - 36 * x[0] * x[1] + 27 * x[1] * x[1]));
	//SiX-Hump Camel函數
	//fx = 4 * x[0] * x[0] - 2.1*pow(x[0], 4) + 1.0 / 3.0*pow(x[0], 6) + x[0] * x[1] - 4 * x[1] * x[1] + 4 * pow(x[1], 4);
	return fx;
}
//**********************************************
//計算每個鳥窩的目標函數值並記錄當前的最優解
//**********************************************
double cs::select_optimal(vector<vector<double>>x)
{
	double fmin = 0;
	fmin = fit_function(x[0]);
	for (int i = 1; i < N_nest; i++)
	{
		if (fmin > fit_function(x[i]))
		{
			fmin = fit_function(x[i]);
			for (int j = 0; j < D_egg; j++)
			{
				nest_best[j] = x[i][j];
			}
		}
	}
	return fmin;
}
//**********************************************
//隨機數R更新鳥窩位置
//**********************************************
vector<vector<double>> cs::update_Rrandomnumber(vector<vector<double>>x)
{
	extern RandomNumber r;       //聲明全局隨機數
	for (int j = 0; j < N_nest; j++)
	{
		for (int k = 0; k < D_egg; k++)
		{
			R = r.decimal(0, 1);
			if (R > pa)
			{
				x[j][k] = r.decimal(nest_low[k], nest_high[k]);
			}
		}
	}
	return x;
}
//**********************************************
//萊維飛行更新每個鳥窩的位置
//**********************************************
vector<vector<double>> cs::update_Levyflight(vector<vector<double>>x, int t)
{
	extern RandomNumber r;       //聲明全局隨機數
	for (int j = 0; j < N_nest; j++)
	{
		for (int k = 0; k < D_egg; k++)
		{
			lambda = r.decimal(1, 3);
			x[j][k] = x[j][k] + cs_alpha * pow(t, -1 * lambda);
			if (x[j][k]< nest_low[k] || x[j][k] > nest_high[k])
			{
				x[j][k] = r.decimal(nest_low[k], nest_high[k]);
			}
		}
	}
	return x;
}

主函數:

#include "pch.h"
#include <iostream>
RandomNumber r;       //隨機數
int main()
{
	clock_t startTime, endTime; //定義程序開始運行時間和結束時間
	startTime = clock();  //計時開始
	cs CS;          //定義布穀鳥種羣
	CS.setParameters();//設置算法參數
	CS.initialize(); //初始化
	CS.fmin[0] = CS.select_optimal(CS.p_t);//選擇初代最優個體
	cout << CS.fmin[0] << endl;
	//循環體
	ofstream out("布穀鳥算法優化結果.txt");
	for (int i = 1; i < size(CS.fmin); i++)
	{
		CS.p_t = CS.update_Levyflight(CS.p_t, i);  //萊維飛行更新鳥窩的位置
		CS.fmin[i] = CS.select_optimal(CS.p_t);
		if (CS.fmin[i] > CS.fmin[i - 1])
		{
			CS.fmin[i] = CS.fmin[i - 1];
		}
		CS.p_t = CS.update_Rrandomnumber(CS.p_t);//隨機數R發現更新鳥窩的位置
		if (CS.fmin[i] > CS.select_optimal(CS.p_t))
		{
			CS.fmin[i] = CS.select_optimal(CS.p_t);
		}
		out << i << fixed << setw(12) << setprecision(5) << CS.fmin[i] << endl;
	}
	out << "最優變量:" << endl;
	for (int ii = 0; ii < size(CS.nest_best); ii++)
	{
		out << "x" << ii << "=" << fixed << setw(12) << setprecision(5) << CS.nest_best[ii] << endl;//輸出最優變量
	}
	out << "最優值=" << fixed << setw(12) << setprecision(5) << CS.fmin[size(CS.fmin) - 1] << endl;
	endTime = clock();//計時結束
	out << "run time:" << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
}

各位閱讀文章的朋友覺得文章可以給個好評或者點贊,大家覺得有問題可以指出來或者發郵箱[email protected]聯繫我!如需要轉載請附上鍊接,謝謝各位朋友!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章