差分演化算法(Differential Evolution )是曾經一度非常熱門的算法,該算法簡單易用,收斂速度快。這篇文章對其進行總結。
算法簡介
所謂的演化算法是一種自適應,並行的全局優化算法,還包括遺傳算法等。
差分演化算法與其他演化算法的最大區別在與差分變異算子的應用。
差分演化算法主要用於求解實數優化問題,一般不用於求解離散問題。
算法流程
算法流程圖如下。
僞代碼流程如下。
下面對每一個具體的階段進行說明。
在初始化階段,對於每個個體的每一維在其自變量範圍內採用均勻隨機初始化。
在變異階段,採用差分變異算子,經典差分算子DE/rand/1的公式如下:
在交叉階段,採用離散重組,常用的二項式雜交算子如下(就是讓子個體和父個體交換某些維度的座標值):
其中的v是變異之後的個體,此處的條件j==jrand是爲了避免交叉後的子個體與父個體完全相同,交叉的效果如下圖。
在選擇階段採用錦標賽一對一選擇,就是父個體和自己的子個體比較,誰優秀誰留下。
實例代碼
隨手擼的簡易版差分演化代碼,目標函數爲f=x^2+y^2。對於每個個體都要進行變異操作,所以不設置變異率這個常量。
#include<iostream>
#include<vector>
#include<time.h>
using namespace std;
const int SIZE = 10; //種羣規模
const int DIMENSION = 2; //目標函數維數
const double F = 0.6; //縮放因子
const double CROSSOVER_RATE = 0.2; //交叉率
const int ITER_NUM = 30; //進化20代
const vector<double> LOWER_BOUND = { -100,-100 }; //每一個維度的上限
const vector<double> UPPER_BOUND = { 100,100 }; //每一個維度的下限
struct Single{ //個體結構
vector<double>m_xreal; //n維座標
double m_fitness; //適應度
Single(){ //構造函數
m_xreal = vector<double>(DIMENSION, 0); //初始化座標
m_fitness = 0; //初始化適應度
}
};
vector<Single>parentpop(SIZE); //父種羣
vector<Single>childpop(SIZE); //子種羣
double RandReal(int left, int right); //得到一個隨機實數
double RandInt(int left, int right); //得到一個隨機整數
void Print(); //打印羣體情況
void InitPop(); //初始化羣體
double CalculateFitness(vector<double>realx); //計算適應度
void ParentSelection(vector<int>&index); //選擇三個不相同的父親個體
void Mutation(vector<int>&index,int i); //變異操作
void BinCrossover(int i); //交叉操作
void Selection(int i); //根據一對一錦標賽選擇個體
int main()
{
srand((unsigned)time(NULL)); //隨機數種子
InitPop();
Print();
cout << endl;
for(int i=0;i<ITER_NUM;i++)
{
for (int j = 0; j < SIZE; j++) //對每一個個體進行操作
{
vector<int>index;
ParentSelection(index);
Mutation(index,j);
BinCrossover(j);
Selection(j);
}
Print();
cout << endl;
}
cin.get();
}
void Print()
{
for (auto var : parentpop)
cout << "coordinate:" << var.m_xreal[0] << "," << var.m_xreal[1] << " fitness:" << var.m_fitness << endl;
}
double RandReal(int left, int right)
{
int range = right - left;
double result(0);
result = rand() % range - 1 + (float)(rand() % 1000) / 1000.0 + left + 1;
return result;
}
double RandInt(int left, int right)
{
int range = right - left;
double result(0);
result = rand() % (range + 1) + left;
return result;
}
void InitPop()
{
for ( int i=0;i<SIZE;i++)
{
for (int j = 0; j < DIMENSION; j++)
parentpop[i].m_xreal[j] = RandReal(LOWER_BOUND[j], UPPER_BOUND[j]);
parentpop[i].m_fitness = CalculateFitness(parentpop[i].m_xreal);
}
}
double CalculateFitness(vector<double>realx)//計算適應值
{
//目標函數f=x1^2+y^2
double result = realx[0] * realx[0] + realx[1] * realx[1];
return result;
}
void ParentSelection(vector<int>&index)//挑選三個不同的父親個體的索引
{
for (int i = 0; i < 3; i++)
{
int r(0);
bool flag (false);
do
{
flag = false;
r = RandInt(0, SIZE - 1);
for (auto var : index)
if (r == var)flag=true;
} while (flag);
index.push_back(r);
}
}
void Mutation(vector<int>&index, int i)
{
int r1 = index[0], r2 = index[1], r3 = index[2];
for (int j = 0; j < DIMENSION; j++)
{
//根據差分變異算子進行變異
childpop[i].m_xreal[j] = parentpop[r1].m_xreal[j] + F*(parentpop[r2].m_xreal[j] - parentpop[r3].m_xreal[j]);
//如果越界,隨機取一個值
if (childpop[i].m_xreal[j] < LOWER_BOUND[j] || childpop[i].m_xreal[j] > UPPER_BOUND[j])
childpop[i].m_xreal[j] = RandReal(LOWER_BOUND[j], UPPER_BOUND[j]);
}
}
void BinCrossover(int i)
{
//設置一個temp值,保證子個體不會與父個體完全相同
int temp = RandInt(0, DIMENSION - 1);
for (int j = 0; j < DIMENSION; j++)
{
if (RandReal(0, 1) > CROSSOVER_RATE && j != temp)
{
childpop[i].m_xreal[j] = parentpop[i].m_xreal[j];
}
}
}
void Selection(int i)//錦標賽選擇
{
parentpop[i].m_fitness = CalculateFitness(parentpop[i].m_xreal);
childpop[i].m_fitness = CalculateFitness(childpop[i].m_xreal);
if (childpop[i].m_fitness <= parentpop[i].m_fitness)
parentpop[i] = childpop[i];
}
http://www1.icsi.berkeley.edu/~storn/code.html#c++c
這個網站是DE算法的官網,有正兒八經的各種語言的DE算法代碼。
謝謝觀看:)