粒子羣優化算法源於對一個簡化鳥羣的模擬。算法中的每個粒子可視爲N維搜索空間中的一個搜索個體,粒子有兩個屬性:速度和位置。粒子的當前位置即爲對應優化問題的一個候選解,粒子的飛行過程即爲該個體的搜索過程。粒子的飛行速度可根據粒子歷史最優位置和種羣歷史最優位置進行動態調整。飛行速度和位置的更新公式如下。
其中w爲慣性系數,C1爲個體經驗係數,C2爲社會經驗係數。爲每個粒子單獨搜尋的最優解,即個體極值,爲粒子羣中最優的個體極值,即當前的全局最優解。算法通過不斷迭代,更新速度和位置。最終得到滿足終止條件的最優解。
我在實踐過程中發現,粒子羣很容易陷入局部最優。想要達到最優解,需要多次運行程序進行嘗試,實際應用中可能非常麻煩。於是我聯想遺傳算法,爲粒子羣算法加入了變異,以跳出局部最優。僞代碼代碼如下。
b ← 取一個0到1之間的隨機數
if b>0.98 //有0.02的機率發生變異
int x ← 0~(維度-1) 之間的隨機數
for j ← 0 to n do //對所有個體
p[j].pos[x] ←隨機數 //將所有粒子x維的座標打亂
end
加入變異機制後,提高了算法的穩定性,對很多連續函數均可以通過一次搜索找到最優解,但問題在於對粒子羣的參數和變異率要求過高,否則收斂很慢,如何調整到最佳值得繼續研究。 下面的代碼中沒有加入變異機制。
#include<iostream>
#include<vector>
#include<time.h>
using namespace std;
const int SIZE = 10; //種羣規模
const int DIMENSION = 2; //目標函數維數
const double W = 0.4; //慣性系數
const double C1 = 2; //個體認知常數
const double C2 = 2; //社會經驗常數
const int ITER_NUM = 30; //進化30代
const vector<double> LOWER_BOUND(DIMENSION, -30); //每一個維度的位置下限
const vector<double> UPPER_BOUND(DIMENSION, 30); //每一個維度的位置上限
const vector<double> MIN_VELO(DIMENSION, -60); //每一個維度的速度下限
const vector<double> MAX_VELO(DIMENSION, 60); //每一個維度的速度上限
struct Single { //個體結構
vector<double>m_x; //n維座標
vector<double>m_v; //速度
double m_fitness; //適應度
vector<double> m_pbest; //個體最優點
Single() { //構造函數
m_x = vector<double>(DIMENSION); //初始化座標
m_v = vector<double>(DIMENSION); //初始化速度
m_pbest = m_x; //初始化最優點
m_fitness = 0; //初始化適應度
}
};
vector<double> gbest(DIMENSION); //全局最優點
vector<Single>population(SIZE); //種羣容器
double RandReal(int left, int right); //得到一個隨機實數
double RandInt(int left, int right); //得到一個隨機整數
void Print(); //打印羣體情況
void InitPop(); //初始化羣體
double CalculateFitness(vector<double>x); //計算適應度
void FreshVelocity(int i); //更新個體速度
void FreshPosition(int i); //更新個體位置
int main() //主函數
{
srand((unsigned)time(NULL)); //隨機數種子
InitPop(); //初始化羣體
gbest = population[0].m_x; //初始化全局最優點
Print(); //打印羣體情況
cout << endl;
for (int i = 0; i < ITER_NUM; i++){ //循環迭代,迭代次數爲常量
for (int j = 0; j < SIZE; j++){ //對每一個個體進行操作
double mybestfit = CalculateFitness(population[j].m_pbest); //計算個體最優點的適應值
double globalfit = CalculateFitness(gbest); //計算全局最優點的適應值
if (population[j].m_fitness < mybestfit) //若個體的適應值優於個體最優點
population[j].m_pbest = population[j].m_x; //更新個體最優點
if (population[j].m_fitness < globalfit) //若個體的適應值優於全局最優點
gbest = population[j].m_x; //更新全局最優點
FreshVelocity(j); //更新個體的速度
FreshPosition(j); //更新個體的位置
}
Print(); //打印羣體情況
cout << endl;
}
cin.get();
}
void Print(){ //函數:打印羣體情況
for (auto var : population) //遍歷每個個體
cout <<"coordinate:" << var.m_x[0] << "," << var.m_x[1] //打印個體位置
<<"velocity:"<<var.m_v[0] << "," << var.m_v[1] //打印個體速度
<<"fitness:" << var.m_fitness << endl; //打印適應值
cout << "globle best:" << CalculateFitness(gbest) << endl; //打印全局最優點的適應值
}
double RandReal(int left, int right) { //函數:得到一個隨機實數
int range = right - left; //計算右界和左界的差值
double result(0); //定義返回變量
result = rand() % range + (float)(rand() % 1000) / 1000.0 + left; //計算得到一個範圍內的隨機實數值
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++){ //遍歷個體的每一維
population[i].m_x[j] = RandReal(LOWER_BOUND[j], UPPER_BOUND[j]); //初始化位置
population[i].m_v[j] = RandReal(MIN_VELO[j], MAX_VELO[j]); //初始化速度
}
population[i].m_fitness = CalculateFitness(population[i].m_x); //計算適應值
population[i].m_pbest = population[i].m_x; //一開始的最優點就是第一個點
}
}
double CalculateFitness(vector<double>x){ //函數:計算適應值
double result(0); //定義返回變量
result = x[0] * x[0] + x[1] * x[1]; //目標函數f=x1^2+y^2
return result; //返回結果
}
void FreshVelocity(int i){ //函數:更新速度
double r1 = RandReal(0, 1); //定義隨機數1
double r2 = RandReal(0, 1); //定義隨機數2
for (int j = 0; j < DIMENSION; j++){ //遍歷個體的每一維
population[i].m_v[j] = //更新速度
W*population[i].m_v[j] + //第一項,慣性系數乘原速度
C1*r1*(population[i].m_pbest[j] - population[i].m_x[j]) + //第二項,個體認知影響
C2*r2*(gbest[j] - population[i].m_x[j]); //第三項,社會經驗影響
if (population[i].m_v[j] < MIN_VELO[j]) //如果速度向下越界
population[i].m_v[j] = MIN_VELO[j]; //速度被賦爲下界限值
if (population[i].m_v[j] > MAX_VELO[j]) //如果速度向上越界
population[i].m_v[j] = MAX_VELO[j]; //速度被賦爲上界限值
}
}
void FreshPosition(int i) { //函數:更新位置
for (int j = 0; j < DIMENSION; j++){ //遍歷個體的每一維
population[i].m_x[j] += population[i].m_v[j]; //新的位置爲原位置加速度
if (population[i].m_x[j] < LOWER_BOUND[j]) //如果位置向下越界
population[i].m_x[j] = LOWER_BOUND[j]; //位置被賦爲下界限值
if (population[i].m_x[j] > UPPER_BOUND[j]) //如果位置向上越界
population[i].m_x[j] = UPPER_BOUND[j]; //位置被賦爲上界限值
}
population[i].m_fitness = CalculateFitness(population[i].m_x); //更新適應值
}