PSO算法优化应用实例(2020.09.24)

考虑下面的优化函数

m i n f ( x 1 , x 2 ) = 0.5 + sin ⁡ 2 x 1 2 + x 2 2 − 0.5 ( 1 + 0.001 ( x 1 2 + x 2 2 ) ) 2 minf(x_1,x_2)=0.5+\frac{\sin^2\sqrt{x_1^2+x_2^2}-0.5}{(1+0.001(x_1^2+x_2^2))^2} minf(x1,x2)=0.5+(1+0.001(x12+x22))2sin2x12+x22 0.5
− 100 ≤ x 1 , x 2 ≤ 100 -100≤x_1,x_2≤100 100x1,x2100
        该优化问题的目标函数是Schaffer F6函数,该函数的全局最小值为f(x1,x2)=0,而最优解为(0,0).
用PSO算法求解该问题,其设计如下:
        (1)群体规模M= 20;
        (2)适应函数取为目标函数;
        (3)邻域结构采用全局邻域结构;
        (4)惯性权重 W=0.9;
        (5)c1 = c2 = 2;
        (6)最大速度Vmax=3;
        (7)粒子群初始化:初始化粒子群中每个粒子的位置随机地产生,每个分量均匀地分布在区间[-100,100]内,速度的每个分量均匀地分布在区间 [-3,3]内;
        (8)算法终止条件:算法达到最大迭代次数4000后终止;
        运行算法30次,每次都能发现问题的最优解或次优解。

PSO算法简要介绍

        PSO算法流程图如下:
在这里插入图片描述
        假定粒子群P = {p1,p2,…,pM},即粒子群由M个粒子组成,其中,M称为群体规模。一个粒子通过其所在的位置和速度来描述。粒子pi在d维解空间的位置表示为xi=(xi`,xi2,…,xid),而速度表示为vi=(vi1,vi2,…,vid)。粒子Pi的个体极值表示为pbesti,即pbesti是粒子pi当前所发现的最好解,pi的局部极值表示为gi,即gi为粒子pi的近邻当前所发现的最好解。另外,PSO算法性能的好坏取决于邻域拓扑结构的选择、参数的设置等。
C++所需代码类如下所示:

头文件 源文件
X X.h /
Particle Particle.h Particle.cpp
Qunti Qunti.h Qunti.cpp

//X.h
#ifndef  X_H
#define X_H
#include "Particle.h"
class X
{
public:
	double x[N];
};
#endif // ! X_H
// Particle.h
#ifndef PARTICLE_H
#define  PARTICLE_H
#define N 2//维数
class Particle
{
public :
	double x[N], v[N];
	Particle();//初始化方法
	void shuchu();//打印函数
};
#endif // !PARTICLE_H
// Particle.cpp
#include "Particle.h"
#include <iostream>
using namespace std;
Particle::Particle()
{
	for (int i = 0; i < N; i++) 
	{
		x[i] = 0;
		v[i] = 0;
	}
}

void Particle::shuchu()
{
	std::cout << "位置x=(";
	for (int i = 0; i < N; i++)
	{
		if(i == N-1)std::cout << x[i];
		else std::cout << x[i]<<",";
	}
	std::cout << ") 速度v=(";
	for (int i = 0; i < N; i++)
	{
		if (i == N - 1)std::cout << v[i];
		else std::cout << v[i] << ",";
	}
	std::cout << ")"<<endl;
}
// Qunti.h
#ifndef QUNTI_H
#define QUNTI_H
#include "Particle.h"
const int M = 20;
class Qunti
{
public:
	Particle p[M];
	void shuchu();//初始化方法
};
#endif
// Qunti.cpp
#include "Qunti.h"
#include <iostream>
using namespace std;
void Qunti::shuchu()
{
	for (int i = 0; i < M; i++)
	{
		std::cout << "粒子"<<i + 1;
		p[i].shuchu();
	}
}

第一步,群体粒子的初始化

       每个粒子位置的每个分量随机均匀地在【-100,100】内产生,速度的每个分量随机均匀地在区间【-3,3】内产生。

//第一步,群体粒子的初始化;
	std::default_random_engine random(time(NULL));
	static std::uniform_real_distribution<double> distribution1(Xmin, std::nextafter(Xmax, DBL_MAX));// C++11提供的实数均匀分布模板类
	static std::uniform_real_distribution<double> distribution2(Vmin, std::nextafter(Vmax, DBL_MAX));// C++11提供的实数均匀分布模板类

	for (int i = 0; i < M; i++)
	{
		for (int j = 0; j < N; j++)
		{
			qt.p[i].x[j] = distribution1(random); //群体粒子位置向量初始化
			qt.p[i].v[j] = distribution2(random);//群体粒子速度向量初始化
			pbest[i].x[j] = qt.p[i].x[j];
			funvalue[i] = Mubiao_fun(qt.p[i].x);
		}
	}
	int minlocation = 0;
	double minfunvalue = funvalue[0];
	for (int i = 0; i < M; i++)
	{
		if (funvalue[i] < minfunvalue)
		{
			minfunvalue = funvalue[i];
			minlocation = i;
		}
	}
	for(int j=0;j<N;j++)
		gbest[j] = qt.p[minlocation].x[j];
	qt.shuchu();

第二步,更新每个粒子的速度和位置

        在PSO算法中,粒子根据以下公式来更新速度和位置:
   { v i ← w ⋅ v i + c 1 ⋅ r a n d ( ) ⋅ ( p b e s t i − x i ) + c 2 ⋅ r a n d ( ) ⋅ ( g i − x i ) , x j = x i + v i , \; \begin{cases} v_i\leftarrow{w\cdot v_i+c_1\cdot rand() \cdot (pbest_i-x_i)+c_2\cdot rand() \cdot(g_i-x_i)}, & \text{} \\ x_j=x_i+v_i,& \text{} \\ \end{cases} { viwvi+c1rand()(pbestixi)+c2rand()(gixi),xj=xi+vi,
        其中,w是一个正常数,成为惯性权重,c1,c2是两个正常数,称为学习因子,rand()是在[0,1]中服从均匀分布的随机数。
        从上面的速度和位置更新公式中可以看出,一个粒子的速度决定了器位置的变化范围。为了防止粒子过快地从搜索空间中的一个区域飞向另一个区域,定义一个参数Vmax,使得粒子每一维的速度都被限制在区间[-Vmax,Vmax]内,也就是说,若粒子pi的速度为vi=(vi1,vi2,…,vid),则使用以下的边界条件:
   { v i j > V m a x    ⟹    v i j = V m a x , j = 1 , . . . , d , v i j < − V m a x    ⟹    v i j = − V m a x , j = 1 , . . . , d , \; \begin{cases} v_{ij} >V_{max} \implies v_{ij} = V_{max}, j=1,...,d, & \text{} \\ v_{ij} <-V_{max} \implies v_{ij} = -V_{max}, j=1,...,d,& \text{} \\ \end{cases} { vij>Vmaxvij=Vmax,j=1,...,d,vij<Vmaxvij=Vmax,j=1,...,d,

        粒子pi更新后的速度是先前速度vi,pbesti-xi和gi-xi的加权矢量和,如下图所示。粒子在解空间内不断跟踪个体极值和局部极值进行搜索,直到满足算法的终止条件为止。
在这里插入图片描述

//更新每个粒子的速度和位置
			for (int j = 0; j < N; j++)
			{
				static std::uniform_real_distribution<double> distribution3(0.0, std::nextafter(1.0, DBL_MAX));// C++11提供的实数均匀分布模板类
				qt.p[i].v[j] = w*qt.p[i].v[j] + c1*distribution3(random)*(pbest[i].x[j] - qt.p[i].x[j]) + c2*distribution3(random)*(gbest[j] - qt.p[i].x[j]);
			}
			for (int j = 0; j < N; j++)
			{
				if (qt.p[i].v[j]> Vmax) qt.p[i].v[j] = Vmax;
				else if (qt.p[i].v[j] < Vmin) qt.p[i].v[j] = Vmin;
			}
			for (int j = 0; j < N; j++)//粒子速度越界调整
			{
				qt.p[i].x[j] += qt.p[i].v[j];
			}
			for (int j = 0; j < N; j++)//粒子位置越界调整
			{
				if (qt.p[i].x[j]> Xmax) qt.p[i].x[j] = Xmax;
				else if (qt.p[i].x[j] < Xmin) qt.p[i].x[j] = Xmin;
			}

第三步,根据粒子的适应值来更新个体的历史极值并计算全局极值

        在PSO算法中,一个粒子的邻域表示群体中与该粒子相邻的粒子的集合,粒子可以与其邻域中的粒子交换信息,邻域的规模决定了信息交换的范围。一个粒子的邻域可以是整个群体,也可以由其周围的少数几个粒子构成,邻域拓扑决定了群体中粒子的相邻关系。三种主要的邻域拓扑:星状拓扑(也称全局邻域拓扑)、坏状拓扑和轮状拓扑。
在这里插入图片描述
        当邻域结构取为全局邻域结构时,粒子pi的局部极值gi成为全局极值,通常记为gbest。通过对以上三种邻域结构进行实验研究,Kennedy得出的结论是:使用全局邻域结构的PSO算法收敛速度较快,但容易陷入局部最优;使用环状邻域结构的PSO算法的收敛速度较慢,但能以较大的概率发现最优解;而使用轮状邻域结构的PSO算法效果较差。

//根据粒子的适应值更新个体极值(历史最优)并计算极值(全局最优)
			if (Mubiao_fun(qt.p[i].x) < Mubiao_fun(pbest[i].x))
			{
				for(int j=0;j<N;j++)
					pbest[i].x[j] =qt.p[i].x[j];
			}
			for (int j = 0; j<N; j++)
				gbest[j] = pbest[i].x[j];
			for (int j = 0; j < M; j++)
			{
				if (Mubiao_fun(pbest[j].x) < Mubiao_fun(gbest))
				{
					for (int k = 0; k<N; k++)
						gbest[k] = pbest[j].x[k];
				}
			}

最后

        C++运行PSO算法求解函数优化的主函数如下:

// main.cpp
#include <iostream>
#include <math.h>
#include <random>
#include<time.h>
using namespace std;
#include "Particle.h"
#include "Qunti.h"
#include "X.h"
double Mubiao_fun(double x[N])
{
    
    
	double f = 0.5 + (pow(sin(sqrt(x[0] * x[0] + x[N-1] * x[N - 1])), 2) - 0.5) / pow((1 + 0.001*pow((x[0] * x[0] + x[N - 1] * x[N - 1]),2)), 2);
	return f;
}
int main()
{
    
    
	std::cout << "Hello world!" << endl;
	double w = 0.9,Xmax = 100,Xmin = -100,Vmax = 3.0,Vmin = -3.0;//位置分量和速度的最大最小值
	int t=0,c1 = 2, c2 = 2,iteratortime = 4000;//t=0表示第一次迭代
	Qunti qt;
	double funvalue[M], gbest[N];//M个粒子对应的函数值数组
	X pbest[M];//pbesti表示粒子pi的个体极值;gi表示pi的局部极值
	for (int i = 0; i < M; i++)
	{
    
    
		 funvalue[i] = 0;//数组初始化
	}

	//第一步,群体粒子的初始化;
	std::default_random_engine random(time(NULL));
	static std::uniform_real_distribution<double> distribution1(Xmin, std::nextafter(Xmax, DBL_MAX));// C++11提供的实数均匀分布模板类
	static std::uniform_real_distribution<double> distribution2(Vmin, std::nextafter(Vmax, DBL_MAX));// C++11提供的实数均匀分布模板类

	for (int i = 0; i < M; i++)
	{
    
    
		for (int j = 0; j < N; j++)
		{
    
    
			qt.p[i].x[j] = distribution1(random); //群体粒子位置向量初始化
			qt.p[i].v[j] = distribution2(random);//群体粒子速度向量初始化
			pbest[i].x[j] = qt.p[i].x[j];
			funvalue[i] = Mubiao_fun(qt.p[i].x);
		}
	}
	int minlocation = 0;
	double minfunvalue = funvalue[0];
	for (int i = 0; i < M; i++)
	{
    
    
		if (funvalue[i] < minfunvalue)
		{
    
    
			minfunvalue = funvalue[i];
			minlocation = i;
		}
	}
	for(int j=0;j<N;j++)
		gbest[j] = qt.p[minlocation].x[j];
	qt.shuchu();
	for (int ite = 0; ite < iteratortime; ite++)
	{
    
    
		for (int i = 0; i < M; i++)
		{
    
    
			//更新每个粒子的速度和位置
			for (int j = 0; j < N; j++)
			{
    
    
				static std::uniform_real_distribution<double> distribution3(0.0, std::nextafter(1.0, DBL_MAX));// C++11提供的实数均匀分布模板类
				qt.p[i].v[j] = w*qt.p[i].v[j] + c1*distribution3(random)*(pbest[i].x[j] - qt.p[i].x[j]) + c2*distribution3(random)*(gbest[j] - qt.p[i].x[j]);
			}
			for (int j = 0; j < N; j++)
			{
    
    
				if (qt.p[i].v[j]> Vmax) qt.p[i].v[j] = Vmax;
				else if (qt.p[i].v[j] < Vmin) qt.p[i].v[j] = Vmin;
			}
			for (int j = 0; j < N; j++)//粒子速度越界调整
			{
    
    
				qt.p[i].x[j] += qt.p[i].v[j];
			}
			for (int j = 0; j < N; j++)//粒子位置越界调整
			{
    
    
				if (qt.p[i].x[j]> Xmax) qt.p[i].x[j] = Xmax;
				else if (qt.p[i].x[j] < Xmin) qt.p[i].x[j] = Xmin;
			}
			//根据粒子的适应值更新个体极值(历史最优)并计算极值(全局最优)
			if (Mubiao_fun(qt.p[i].x) < Mubiao_fun(pbest[i].x))
			{
    
    
				for(int j=0;j<N;j++)
					pbest[i].x[j] =qt.p[i].x[j];
			}
			for (int j = 0; j<N; j++)
				gbest[j] = pbest[i].x[j];
			for (int j = 0; j < M; j++)
			{
    
    
				if (Mubiao_fun(pbest[j].x) < Mubiao_fun(gbest))
				{
    
    
					for (int k = 0; k<N; k++)
						gbest[k] = pbest[j].x[k];
				}
			}
		}
		if (ite == iteratortime - 1)
		{
    
    
			cout << "迭代" << iteratortime << "次后的群体粒子如下:" << endl;
			qt.shuchu();
		}
	}
	std::cout << "最优解位置为:x=(";
	for (int j = 0; j < N; j++)
	{
    
    
		if(j == N-1)std::cout << gbest[j]<<")"<<endl;
		else std::cout << gbest[j] << ",";
	}
	system("pause");
	return 0;
}

运行结果如下图所示:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/jing_zhong/article/details/108769867