本次带来粒子群优化算法和差分进化算法,解决单目标连续优化问题。算法的benchmark全部来自http://www.ntu.edu.sg/home/epnsugan/index_files/CEC2018/CEC2018.htm。这上面有30个测试函数,每个函数基本上有2维、10维、20维、30维、50维、100维的版本,提供了一个c语言的function文件,包含了全部benchmark函数,测试可直接调用。这也是本人的一项课程的课程作业,如有错误,欢迎指出,共同学习!谢谢。
整个项目源码可在github下载:
https://github.com/ZeusYang/AILearning/tree/master/SingleObjectiveOpt
简介
粒子群优化算法( Particle Swarm Optimization,PSO)是进化计算的一个分支,是一种模拟自然界的生物活动的随机搜索算法。PSO模拟了自然界鸟群捕食和鱼群捕食的过程。通过群体中的协作寻找到问题的全局最优解。它是1995年由美国学者Eberhart和Kennedy提出的,现在已经广泛应用于各种工程领域的优化问题之中。
PSO从鸟群觅食模型中得到启示并用于解决优化问题。PSO中,每个优化问题的解都是搜索空间中的一只鸟。我们称之为“粒子”。所有的粒子都有一个由被优化的函数决定的适应值(fitness value),每个粒子还有一个速度决定他们飞翔的方向和距离。然后粒子们就追随当前的最优粒子在解空间中搜索。
算法流程及伪代码
PSO 初始化为一群随机粒子(随机解)。然后通过迭代找到最优解。在每一次迭代中,粒子通过跟踪两个”极值”来更新自己。第一个就是粒子本身所找到的最优解,这个解叫做个体极值pBest。另一个极值是整个种群目前找到的最优解,这个极值是全局极值gBest。另外也可以不用整个种群而只是用其中一部分作为粒子的邻居,那么在所有邻居中的极值就是局部极值。
更新公式及其参数说明
每个粒子个体的速度和位置更新的公式如下:
v[]和present[]为个体的速度数组和位置数组,每个粒子都有它相应的速度和位置,速度的维度和变量的维度保持一致。w为惯性权重,它控制着前一速度对当前速度的影响,用于平衡算法的探索和开发能力。c1和c2是加速系数,它代表了粒子向自身极值pBest和全局极值gBest推进的加速权值。除了这些参数之外,还有种群规模N,也就是用多少个粒子实例去优化,它影响着算法的搜索能力和计算量。结束条件可以用一定的迭代次数替换,达到一定的迭代次数之后算法就会收敛,不再推进。
v[] = w * v[] + c1 * rand() * (pbest[] - present[]) + c2 * rand() * (gbest[] - present[])
present[] = present[] + v[]
PSO对种群规模要求不高,一般取20-40就可以达到很好的求解效果,不过对于比较难的问题或者特定类别的问题,粒子数可以取到100或200。
惯性权重我这里取0.5,代表前一速度对当前速度有一半的贡献。
c1和c2通常都等于2.0,代表着对两个引导方向的同等重视 。
终止条件决定算法运行的结束,由具体的应用和问题本身确定。有几种做法:
(1)将最大循环数设定为500,1000,5000,或者最大的函数评估次数,等等;
(2)也可以使用算法求解得到一个可接受的解作为终止条件;
(3)或者是当算法在很长一段迭代中没有得到任何改善,则可以终止算法.
简介
差分进化算法(DE)是Rainer Storn 和Kenneth Price在1996 年为求解切比雪夫多项式而提出。DE是一种随机的并行直接搜索算法,它可对非线性不可微连续空间函数进行最小化,以其易用性、稳健性和强大的全局寻优能力在多个领域取得成功。
DE也是基于群体的启发式搜索算法,群中的每个个体对应一个解向量。差分进化算法的进化流程则与遗传算法非常类似,都包括变异、杂交和选择操作,但这些操作的具体定义与遗传算法有所不同。
算法流程及伪码
DE算法通过采用浮点矢量进行编码生成种群个体。
在DE算法寻优的过程中,
首先,从父代个体间选择两个个体进行向量做差生成差分矢量;
其次,选择另外一个个体与差分矢量求和生成实验个体;
然后,对父代个体与相应的实验个体进行交叉操作,生成新的子代个体;
最后在父代个体和子代个体之间进行选择操作,将符合要求的个体保存到下一代群体中去。
具体流程如下:
(1)确定差分进化算法控制参数,确定适应度函数。差分进化算法控制参数包括:种群大小NP、缩放因子F与杂交概率CR。
(2)随机产生初始种群。
(3)对初始种群进行评价,即计算初始种群中每个个体的适应度值。
(4)判断是否达到终止条件或进化代数达到最大。若是,则终止进化,将得到最佳个体作为最优解输出;若否,继续。
(5)进行变异和交叉操作,得到中间种群。
(6)在原种群和中间种群中选择个体,得到新一代种群。
(7)进化代数g=g+1,转步骤(4)
更新公式及其参数说明
差异演化算法主要涉及群体规模M 、缩放因子F以及交叉概率CR三个参数的设定。
M:一般介于5×n 与10×n 之间, 但不能少于4, 否则无法进行变异操作,n是维度;
F :一般在[ 0, 2 ]之间选择, 通常取0. 5;
CR:一般在[ 0, 1 ]之间选择, 比较好的选择应在0. 3 左右, CR 大些收敛速度会加快, 但易发生早熟现象。
srand((unsigned)time(NULL));
int population = static_cast<int>(getParameters(tr("population")));//种群大小
int dimension = static_cast<int>(getParameters(tr("dimension")));//维度
int generation = static_cast<int>(getParameters(tr("generation")));//迭代次数
double lowbounding = getParameters(tr("lowbounding"));
double upbounding = getParameters(tr("upbounding"));
group.clear();
group.resize(generation,std::vector<std::vector<double> >(population,
std::vector<double>(dimension,0.0)));
for (int i = 0; i < population; i++) {
for (int j = 0; j < dimension; j++) {
group[0][i][j] = lowbounding +
(double)(rand() / (double)RAND_MAX) * (upbounding - lowbounding);
}
}
void PSOAlgorithm::update(int curGeneration)
{//更新速度和位置
int dimension = static_cast<int>(getParameters(tr("dimension")));
int population = static_cast<int>(getParameters(tr("population")));
double w = getParameters(tr("w"));//惯性权重
double c1 = getParameters(tr("c1"));//加速系数
double c2 = getParameters(tr("c2"));//加速系数
double upbounding = getParameters(tr("upbounding"));//上界
double lowbounding = getParameters(tr("lowbounding"));//下界
for(auto i = 0;i < population;++i){
for(auto j = 0;j < dimension;++j){
double pre = velocity[i][j];
//更新速度
velocity[i][j] = w*pre +
c1*(double)(rand()/(double)RAND_MAX)*
(pBest[i][j] - group[curGeneration - 1][i][j])
+
c2*(double)(rand()/(double)RAND_MAX)*
(gBest[j] - group[curGeneration - 1][i][j]);
if(velocity[i][j] > 200)velocity[i][j] = 200;
//位置更新
group[curGeneration][i][j] = group[curGeneration-1][i][j] + velocity[i][j];
//边界处理
if(group[curGeneration][i][j] > upbounding)
group[curGeneration][i][j] = upbounding;
if(group[curGeneration][i][j] < lowbounding)
group[curGeneration][i][j] = lowbounding;
}
}
}
void PSOAlgorithm::curAndgloBest(int curGeneration)
{//计算当前最优解和全局最优解
int population = static_cast<int>(getParameters(tr("population")));
int dimension = static_cast<int>(getParameters(tr("dimension")));
int func_num = static_cast<int>(getParameters(tr("function")));//函数编号
for (auto i = 0; i < population; ++i) {
double value = functions(group[curGeneration][i], func_num, dimension);
if (value < functions(pBest[i], func_num, dimension))
pBest[i] = group[curGeneration][i];
if (functions(pBest[i], func_num, dimension) < functions(gBest, func_num, dimension)){
gBest = pBest[i];
bestValue.first = i;
bestValue.second = functions(pBest[i], func_num, dimension);
}
}
}
void PSOAlgorithm::process()
{
curAndgloBest(0);//计算初始代最优解
int generation = static_cast<int>(getParameters(tr("generation")));//迭代次数
for (auto current = 1; current < generation; ++current) {
update(current);
curAndgloBest(current);
calcCurrentValue(pBest);
if(generation <= 100 || (generation > 100 && generation % 2 == 0))
emit frameUpdate(avgValue,currentValue,minY,maxY,current,generation,bestValue);
}
emit processFinished(getResult(gBest));
}
srand((unsigned)time(NULL));
int population = static_cast<int>(getParameters(tr("population")));//种群大小
int dimension = static_cast<int>(getParameters(tr("dimension")));//维度
int generation = static_cast<int>(getParameters(tr("generation")));//迭代次数
double lowbounding = getParameters(tr("lowbounding"));
double upbounding = getParameters(tr("upbounding"));
group.clear();
group.resize(generation,std::vector<std::vector<double> >(population,
std::vector<double>(dimension,0.0)));
for (int i = 0; i < population; i++) {
for (int j = 0; j < dimension; j++) {
group[0][i][j] = lowbounding +
(double)(rand() / (double)RAND_MAX) * (upbounding - lowbounding);
}
}
void DEAlgorithm::process()
{
int population = static_cast<int>(getParameters(tr("population")));//种群大小
int dimension = static_cast<int>(getParameters(tr("dimension")));//维度
int generation = static_cast<int>(getParameters(tr("generation")));//迭代次数
double lowbounding = getParameters(tr("lowbounding"));
double upbounding = getParameters(tr("upbounding"));
double DE_F = getParameters(tr("F"));//缩放因子
double DE_CR = getParameters(tr("CR"));//交叉概率
int func_num = static_cast<int>(getParameters(tr("function")));//函数编号
double bestSolution = 100000000;//目前全局最优解
//开始迭代
for(auto t=1;t//变异
for(int i=0;i//随机选三个向量
std::vector<bool> selected(population,false);
selected[i] = true;
int pick[3];
for(auto k=0;k<3;k++){
pick[k] = rand() % population;
while(selected[pick[k]])pick[k] = rand() % population;
selected[pick[k]] = true;
}
for(auto j=0;j//变异操作公式
group[t][i][j] = group[t-1][pick[0]][j] +
DE_F * (group[t-1][pick[1]][j] - group[t-1][pick[2]][j]);
//边界处理
if(group[t][i][j]else if(group[t][i][j]>upbounding) group[t][i][j] = upbounding;
}
}
//交叉
for(auto i=0;ifor(auto j=0;jif(((rand() % 100) / 100.0) <= DE_CR || j == (rand() % dimension))
group[t][i][j] = group[t][i][j];
else
group[t][i][j] = group[t-1][i][j];
}
}
//选择替换,贪心策略
for(auto i=0;iif(functions(group[t][i],func_num,dimension) <=
functions(group[t-1][i],func_num,dimension)){
group[t][i] = group[t][i];
bestSolution =
bestSolution < functions(group[t][i],func_num,dimension)
? bestSolution : functions(group[t][i],func_num,dimension);
if(bestSolution < functions(group[t][i],func_num,dimension)){
bestValue.first = i;
bestValue.second = bestSolution;
solution = group[t][i];
}
}
else{
group[t][i] = group[t-1][i];
}
}
//计算当前种群的全部值
calcCurrentValue(group[t]);
if(generation > 300 && generation % 2 == 0){
emit frameUpdate(avgValue,currentValue,minY,maxY,t,generation,bestValue);
}
else{
emit frameUpdate(avgValue,currentValue,minY,maxY,t,generation,bestValue);
}
}
emit processFinished(getResult(solution));
}
以上只是算法的核心部分,更多的细节请看源码。
用Qt做了个可视化,可以调节参数,显示了种群个体的每个个体的适应值以及整个种群的适应值收敛曲线。
粒子群算法:
可以看到最后粒子都收敛了,在同一条直线上:
差分进化算法:
总的来说,差分进化算法略占优势,收敛的速度也比较快,粒子群算法比较没那么稳定.
整个项目源码可在github下载:https://github.com/ZeusYang/AILearning/tree/master/SingleObjectiveOpt