C语言实现粒子群算法(PSO)二

简介: 上一回说了基本粒子群算法的实现,并且给出了C语言代码。这一篇主要讲解影响粒子群算法的一个重要参数---w。我们已经说过粒子群算法的核心的两个公式为:Vid(k+1)=w*Vid(k)+c1*r1*(Pid(k)-Xid(k))+c2*r2*(Pgd(k)-Xid(k))Xid(k+1) = Xid(k) + Vid(k+1)标红的w即是本次我们要讨论的参数。

上一回说了基本粒子群算法的实现,并且给出了C语言代码。这一篇主要讲解影响粒子群算法的一个重要参数---w。我们已经说过粒子群算法的核心的两个公式为:

Vid(k+1)=w*Vid(k)+c1*r1*(Pid(k)-Xid(k))+c2*r2*(Pgd(k)-Xid(k))
Xid(k+1) = Xid(k) + Vid(k+1)

标红的w即是本次我们要讨论的参数。之前w是不变的(默认取1),而现在w是变化的,w称之为惯性权重,体现的是粒子继承先前速度的能力。 经验表明:一个较大的惯性权重有利于全局搜索,而一个较小的惯性权重则更有利于局部搜索。为了更好地平衡算法的全局搜索能力与局部搜索能力,Shi.Y提出了线性递减惯性权重(LDIW)
 即:w(k) = w_end + (w_start- w_end)*(Tmax-k)/Tmax。其中w_start 为初始惯性权重,w_end 为迭代至最大次数时的惯性权重;k为当前迭代次数, Tmax为最大迭代次数。一般来说,w_start=0.9,w_end=0.4时,算法的性能最好。这样随着迭代的进行,惯性权重从0.9递减到0.4,迭代初期较大的惯性权重使算法保持了较强的全局搜索能力。而迭代后期较小的惯性权重有利于算法进行更精确的局部搜索。线性惯性权重,只是一种经验做法,常用的惯性权重还包括 以下几种。
 (3) w(k) = w_start - (w_start-w_end)*(k/Tmax)^2
 (4) w(k) = w_start + (w_start-w_end)*(2*k/Tmax - (k/Tmax)^2)
 (5) w(k) = w_end*(w_start/w_end)^(1/(1+c*k/Tmax)) ,c为常数,比如取10等。
 本例的目的就是比较这5种不同的w取值,对于PSO寻优的影响。比较的方法为每种w取值,重复实验若干次(比如100次),比较平均最优解的大小,陷入次优解的次数,以及接近最优解的次数。 这样对于5种方法的优劣可以有一个直观的比较。

代码如下:

 

/*
 * 使用C语言实现粒子群算法(PSO) 改进版本
 * 参考自《MATLAB智能算法30个案例分析》
 * update: 16/12/3
 * 主要改进的方面体现在w的选择上面
* 本例的寻优非线性函数为
 * f(x,y) = sin(sqrt(x^2+y^2))/(sqrt(x^2+y^2)) + exp((cos(2*PI*x)+cos(2*PI*y))/2) - 2.71289
 * 该函数有很多局部极大值点,而极限位置为(0,0),在(0,0)附近取得极大值
 */
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#define c1 1.49445 //加速度因子一般是根据大量实验所得
#define c2 1.49445
#define maxgen 300  // 迭代次数
#define repeat 100 // 重复实验次数
#define sizepop 20 // 种群规模
#define popmax 2 // 个体最大取值
#define popmin -2 // 个体最小取值
#define Vmax 0.5 // 速度最大值
#define Vmin -0.5 //速度最小值
#define dim 2 // 粒子的维数
#define w_start 0.9
#define w_end 0.4
#define PI 3.1415926 //圆周率

double pop[sizepop][dim]; // 定义种群数组
double V[sizepop][dim]; // 定义种群速度数组
double fitness[sizepop]; // 定义种群的适应度数组
double result[maxgen];  //定义存放每次迭代种群最优值的数组
double pbest[sizepop][dim];  // 个体极值的位置
double gbest[dim]; //群体极值的位置
double fitnesspbest[sizepop]; //个体极值适应度的值
double fitnessgbest; // 群体极值适应度值
double genbest[maxgen][dim]; //每一代最优值取值粒子

//适应度函数
double func(double * arr)
{
    double x = *arr; //x 的值
    double y = *(arr+1); //y的值
    double fitness = sin(sqrt(x*x+y*y))/(sqrt(x*x+y*y)) + exp((cos(2*PI*x)+cos(2*PI*y))/2) - 2.71289;
    return fitness;

}    
// 种群初始化
void pop_init(void)
{
    for(int i=0;i<sizepop;i++)
    {
        for(int j=0;j<dim;j++)
        {
            pop[i][j] = (((double)rand())/RAND_MAX-0.5)*4; //-2到2之间的随机数
            V[i][j] = ((double)rand())/RAND_MAX-0.5; //-0.5到0.5之间
        }
        fitness[i] = func(pop[i]); //计算适应度函数值
    }
}
// max()函数定义
double * max(double * fit,int size)
{
    int index = 0; // 初始化序号
    double max = *fit; // 初始化最大值为数组第一个元素
    static double best_fit_index[2];
    for(int i=1;i<size;i++)
    {
        if(*(fit+i) > max)
            max = *(fit+i);
            index = i;
    }
    best_fit_index[0] = index;
    best_fit_index[1] = max;
    return best_fit_index;

}
// 迭代寻优,传入的参数为一个整数,取值为1到5,分别代表5种不同的计算w的方法
void PSO_func(int n)
{
    pop_init();
    double * best_fit_index; // 用于存放群体极值和其位置(序号)
    best_fit_index = max(fitness,sizepop); //求群体极值
    int index = (int)(*best_fit_index);
    // 群体极值位置
    for(int i=0;i<dim;i++)
    {
        gbest[i] = pop[index][i];
    }
    // 个体极值位置
    for(int i=0;i<sizepop;i++)
    {
        for(int j=0;j<dim;j++)
        {
            pbest[i][j] = pop[i][j];
        }
    }
    // 个体极值适应度值
    for(int i=0;i<sizepop;i++)
    {
        fitnesspbest[i] = fitness[i];
    }
    //群体极值适应度值
    double bestfitness = *(best_fit_index+1);
    fitnessgbest = bestfitness;

    //迭代寻优
    for(int i=0;i<maxgen;i++)
    {
        for(int j=0;j<sizepop;j++)
        {
            //速度更新及粒子更新
            for(int k=0;k<dim;k++)
            {
                // 速度更新
                double rand1 = (double)rand()/RAND_MAX; //0到1之间的随机数
                double rand2 = (double)rand()/RAND_MAX;
                double w;
                double Tmax = (double)maxgen;
                switch(n)
                {
                    case 1:
                        w = 1;
                    case 2:
                        w = w_end + (w_start - w_end)*(Tmax-i)/Tmax;
                    case 3:
                        w = w_start -(w_start-w_end)*(i/Tmax)*(i/Tmax);
                    case 4:
                        w = w_start + (w_start-w_end)*(2*i/Tmax-(i/Tmax)*(i/Tmax));
                    case 5:
                        w = w_end*(pow((w_start/w_end),(1/(1+10*i/Tmax))));
                    default:
                        w = 1;
                }
                V[j][k] = w*V[j][k] + c1*rand1*(pbest[j][k]-pop[j][k]) + c2*rand2*(gbest[k]-pop[j][k]);
                if(V[j][k] > Vmax)
                    V[j][k] = Vmax;
                if(V[j][k] < Vmin)
                    V[j][k] = Vmin;
                // 粒子更新
                pop[j][k] = pop[j][k] + V[j][k];
                if(pop[j][k] > popmax)
                    pop[j][k] = popmax;
                if(pop[j][k] < popmin)
                    pop[j][k] = popmin;
            }
            fitness[j] = func(pop[j]); //新粒子的适应度值
        }
        for(int j=0;j<sizepop;j++)
        {
            // 个体极值更新
            if(fitness[j] > fitnesspbest[j])
            {
                for(int k=0;k<dim;k++)
                {
                    pbest[j][k] = pop[j][k];
                }
                fitnesspbest[j] = fitness[j];
            }
            // 群体极值更新
            if(fitness[j] > fitnessgbest)
            {
                for(int k=0;k<dim;k++)
                    gbest[k] = pop[j][k];
                fitnessgbest = fitness[j];
            }
        }
        for(int k=0;k<dim;k++)
        {
            genbest[i][k] = gbest[k]; // 每一代最优值取值粒子位置记录
        }
        result[i] = fitnessgbest; // 每代的最优值记录到数组
    }
}

// 主函数
int main(void)
{
    clock_t start,finish; //程序开始和结束时间
    start = clock(); //开始计时
    srand((unsigned)time(NULL)); // 初始化随机数种子
    for(int i=1;i<=5;i++)
    {
        int near_best = 0; // 接近最优解的次数
        double best_sum = 0; // 重复最优值求和
        double best = 0; // 重复实验得到的最优解
        for(int j=0;j<repeat;j++)
        {
            PSO_func(i); // 第i种w参数取值
            double * best_fit_index = max(result,maxgen);
            double best_result = *(best_fit_index+1); //最优解
            if(best_result > 0.95)
                near_best++;
            if(best_result>best)
                best = best_result;
            best_sum += best_result;
        }
        double average_best = best_sum/repeat; //重复实验平均最优值
        printf("w参数的第%d种方法:\n",i);
        printf("重复实验%d次,每次实验迭代%d次,接近最优解的实验次数为%d次,求得最优值为:%lf,平均最优值为:%lf\n",repeat,maxgen,near_best,best,average_best);
    }
    finish = clock(); //结束时间
    double duration = (double)(finish - start)/CLOCKS_PER_SEC; // 程序运行时间
    printf("程序运行耗时:%lf\n",duration);
    return 0;
}

 

程序运行结果如下:

从实验的结果来看,第3种w的取法,无论是接近最优解的的次数,最优值大小,还是平均最优值,都是5种里面最好的。其原因解释如下:通过w的表达式可以看出,前期w变化较慢,取值较大,维持了算法的全局搜索能力;后期w变化变化较快,极大地提高了算法的局部搜索能力寻优能力,从而取得了很好的求解效果。

从总体上来看,在大部分的情况下,无论w是5种里面哪种取法,得到的结果都很好地接近实际的最优解,这说明了粒子群算法的搜索寻优能力还是很强的。

 

热爱编程,热爱机器学习! github:http://www.github.com/Lyrichu github blog:http://Lyrichu.github.io 个人博客站点:http://www.movieb2b.com(不再维护)
目录
相关文章
|
20天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
30 1
|
1月前
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
55 4
|
19天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
20天前
|
存储 缓存 算法
C语言在实现高效算法方面的特点与优势,包括高效性、灵活性、可移植性和底层访问能力
本文探讨了C语言在实现高效算法方面的特点与优势,包括高效性、灵活性、可移植性和底层访问能力。文章还分析了数据结构的选择与优化、算法设计的优化策略、内存管理和代码优化技巧,并通过实际案例展示了C语言在排序和图遍历算法中的高效实现。
40 2
|
20天前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
39 1
|
20天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
49 1
|
1月前
|
存储 算法 数据管理
C语言算法复杂度
【10月更文挑战第20天】
C语言算法复杂度
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
93 8
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
100 7
|
2月前
|
算法
基于粒子群算法的分布式电源配电网重构优化matlab仿真
本研究利用粒子群算法(PSO)优化分布式电源配电网重构,通过Matlab仿真验证优化效果,对比重构前后的节点电压、网损、负荷均衡度、电压偏离及线路传输功率,并记录开关状态变化。PSO算法通过迭代更新粒子位置寻找最优解,旨在最小化网络损耗并提升供电可靠性。仿真结果显示优化后各项指标均有显著改善。