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

简介: 最近在温习C语言,看的书是《C primer Plus》,忽然想起来以前在参加数学建模的时候,用过的一些智能算法,比如遗传算法、粒子群算法、蚁群算法等等。当时是使用MATLAB来实现的,而且有些MATLAB自带了工具箱,当时有些只是利用工具箱求最优解问题,没有自己动手亲自去实现一遍,现在都忘的差不多了。

最近在温习C语言,看的书是《C primer Plus》,忽然想起来以前在参加数学建模的时候,用过的一些智能算法,比如遗传算法、粒子群算法、蚁群算法等等。当时是使用MATLAB来实现的,而且有些MATLAB自带了工具箱,当时有些只是利用工具箱求最优解问题,没有自己动手亲自去实现一遍,现在都忘的差不多了。我觉得那样层次实在是很浅没有真正理解算法的核心思想。本着“纸上得来终觉浅,绝知此事要躬行”的态度,我决定现在重新复习一遍算法,然后手工用C语言重新实现一遍。说做就做,我第一个实现的算法是相对来说比较简单的粒子群算法(与遗传算法等相比,至少我自己觉得实现要简单一些)。

      首先简单介绍一下启发式算法和智能算法。粒子群算法、遗传算法等都是从传统的搜索算法演变而来的启发式算法。启发式算法(heuristic algorithm)是相对于最优化算法提出的。一个问题的最优算法求得该问题每个实例的最优解。启发式算法可以这样定义:一个基于直观或经验构造的算法,在可接受的花费(指计算时间和空间)下给出待解决组合优化问题每一个实例的一个可行解,该可行解与最优解的偏离程度一般不能被预计,但是通常情况下启发式算法可以给出接近最优解的不错的解,但是无法保证每次它都可以得到很好的近似解。启发式算法中有一类被称之为智能算法,所谓"智能"二字,指的是这种算法是通过模仿大自然中的某种生物或者模拟某种现象而抽象得到的算法,比如遗传算法就是模拟自然界生物自然选择,优胜劣汰,适者生存而得到的进化算法,粒子群是源于对于鸟类捕食行为的研究,而模拟退火算法则是根据物理学中固体物质的退火过程抽象得到的优化算法。智能算法兴起于上个世纪80年代左右,之后就一直发展迅速,除了传统的智能算法之外,近几年又涌现出了一些新的算法比如鱼群算法、蜂群算法等。

      言归正传,下面来介绍今天的主角:粒子群算法。粒子群算法的基本原理如下(参考《MATLAB智能算法30个案例分析》):

      假设在一个D维的搜索空间中,由n个粒子组成的种群X=(X1,X2,..,Xn),其中第i个粒子表示为一个D维的向量Xi=(xi1,xi2,xiD),代表第i个粒子在D维搜索空间中的位置,亦代表问题的一个潜在解。根据目标函数即可以计算出每个粒子位置Xi对应的适应度值。第i个粒子的速度为Vi = (Vi1,Vi2,...,ViD),其个体极值为Pi=(Pi1,Pi2,...,PiD),种群的群体极值为Pg=(Pg1,Pg2,...,PgD)。在每次迭代的过程中,粒子通过个体极值和群体极值更新自身的速度和位置,即:

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为惯性权重,如果不考虑可以默认为1,后面还会再详细讨论w对于PSO的影响。d=1,2,..,D;i=1,2,...,n;k为当前迭代次数;Vid为粒子的速度;c1和c2是非负的常数,称为加速度因子;r1和r2是分布于[0,1]之间的随机数。为了防止粒子的盲目搜索,一般建议将其位置和速度限制在一定的区间内。

      下面是我用C语言实现的求一个二元函数最大值的粒子群算法:

 

  1 /*
  2  * 使用C语言实现粒子群算法(PSO)
  3  * 参考自《MATLAB智能算法30个案例分析》
  4  * update: 16/12/3
  5 * 本例的寻优非线性函数为
  6  * 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
  7  * 该函数有很多局部极大值点,而极限位置为(0,0),在(0,0)附近取得极大值
  8  */
  9 #include<stdio.h>
 10 #include<stdlib.h>
 11 #include<math.h>
 12 #include<time.h>
 13 #define c1 1.49445 //加速度因子一般是根据大量实验所得
 14 #define c2 1.49445
 15 #define maxgen 300  // 迭代次数
 16 #define sizepop 20 // 种群规模
 17 #define popmax 2 // 个体最大取值
 18 #define popmin -2 // 个体最小取值
 19 #define Vmax 0.5 // 速度最大值
 20 #define Vmin -0.5 //速度最小值
 21 #define dim 2 // 粒子的维数
 22 #define PI 3.1415926 //圆周率
 23 
 24 double pop[sizepop][dim]; // 定义种群数组
 25 double V[sizepop][dim]; // 定义种群速度数组
 26 double fitness[sizepop]; // 定义种群的适应度数组
 27 double result[maxgen];  //定义存放每次迭代种群最优值的数组
 28 double pbest[sizepop][dim];  // 个体极值的位置
 29 double gbest[dim]; //群体极值的位置
 30 double fitnesspbest[sizepop]; //个体极值适应度的值
 31 double fitnessgbest; // 群体极值适应度值
 32 double genbest[maxgen][dim]; //每一代最优值取值粒子
 33 
 34 //适应度函数
 35 double func(double * arr)
 36 {
 37     double x = *arr; //x 的值
 38     double y = *(arr+1); //y的值
 39     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;
 40     return fitness;
 41 
 42 }    
 43 // 种群初始化
 44 void pop_init(void)
 45 {
 46     for(int i=0;i<sizepop;i++)
 47     {
 48         for(int j=0;j<dim;j++)
 49         {
 50             pop[i][j] = (((double)rand())/RAND_MAX-0.5)*4; //-2到2之间的随机数
 51             V[i][j] = ((double)rand())/RAND_MAX-0.5; //-0.5到0.5之间
 52         }
 53         fitness[i] = func(pop[i]); //计算适应度函数值
 54     }
 55 }
 56 // max()函数定义
 57 double * max(double * fit,int size)
 58 {
 59     int index = 0; // 初始化序号
 60     double max = *fit; // 初始化最大值为数组第一个元素
 61     static double best_fit_index[2];
 62     for(int i=1;i<size;i++)
 63     {
 64         if(*(fit+i) > max)
 65             max = *(fit+i);
 66             index = i;
 67     }
 68     best_fit_index[0] = index;
 69     best_fit_index[1] = max;
 70     return best_fit_index;
 71 
 72 }
 73 // 迭代寻优
 74 void PSO_func(void)
 75 {
 76     pop_init();
 77     double * best_fit_index; // 用于存放群体极值和其位置(序号)
 78     best_fit_index = max(fitness,sizepop); //求群体极值
 79     int index = (int)(*best_fit_index);
 80     // 群体极值位置
 81     for(int i=0;i<dim;i++)
 82     {
 83         gbest[i] = pop[index][i];
 84     }
 85     // 个体极值位置
 86     for(int i=0;i<sizepop;i++)
 87     {
 88         for(int j=0;j<dim;j++)
 89         {
 90             pbest[i][j] = pop[i][j];
 91         }
 92     }
 93     // 个体极值适应度值
 94     for(int i=0;i<sizepop;i++)
 95     {
 96         fitnesspbest[i] = fitness[i];
 97     }
 98     //群体极值适应度值
 99     double bestfitness = *(best_fit_index+1);
100     fitnessgbest = bestfitness;
101 
102     //迭代寻优
103     for(int i=0;i<maxgen;i++)
104     {
105         for(int j=0;j<sizepop;j++)
106         {
107             //速度更新及粒子更新
108             for(int k=0;k<dim;k++)
109             {
110                 // 速度更新
111                 double rand1 = (double)rand()/RAND_MAX; //0到1之间的随机数
112                 double rand2 = (double)rand()/RAND_MAX;
113                 V[j][k] = V[j][k] + c1*rand1*(pbest[j][k]-pop[j][k]) + c2*rand2*(gbest[k]-pop[j][k]);
114                 if(V[j][k] > Vmax)
115                     V[j][k] = Vmax;
116                 if(V[j][k] < Vmin)
117                     V[j][k] = Vmin;
118                 // 粒子更新
119                 pop[j][k] = pop[j][k] + V[j][k];
120                 if(pop[j][k] > popmax)
121                     pop[j][k] = popmax;
122                 if(pop[j][k] < popmin)
123                     pop[j][k] = popmin;
124             }
125             fitness[j] = func(pop[j]); //新粒子的适应度值
126         }
127         for(int j=0;j<sizepop;j++)
128         {
129             // 个体极值更新
130             if(fitness[j] > fitnesspbest[j])
131             {
132                 for(int k=0;k<dim;k++)
133                 {
134                     pbest[j][k] = pop[j][k];
135                 }
136                 fitnesspbest[j] = fitness[j];
137             }
138             // 群体极值更新
139             if(fitness[j] > fitnessgbest)
140             {
141                 for(int k=0;k<dim;k++)
142                     gbest[k] = pop[j][k];
143                 fitnessgbest = fitness[j];
144             }
145         }
146         for(int k=0;k<dim;k++)
147         {
148             genbest[i][k] = gbest[k]; // 每一代最优值取值粒子位置记录
149         }
150         result[i] = fitnessgbest; // 每代的最优值记录到数组
151     }
152 }
153 
154 // 主函数
155 int main(void)
156 {
157     clock_t start,finish; //程序开始和结束时间
158     start = clock(); //开始计时
159     srand((unsigned)time(NULL)); // 初始化随机数种子
160     PSO_func();
161     double * best_arr;
162     best_arr = max(result,maxgen);
163     int best_gen_number = *best_arr; // 最优值所处的代数
164     double best = *(best_arr+1); //最优值
165     printf("迭代了%d次,在第%d次取到最优值,最优值为:%lf.\n",maxgen,best_gen_number+1,best);
166     printf("取到最优值的位置为(%lf,%lf).\n",genbest[best_gen_number][0],genbest[best_gen_number][1]);
167     finish = clock(); //结束时间
168     double duration = (double)(finish - start)/CLOCKS_PER_SEC; // 程序运行时间
169     printf("程序运行耗时:%lf\n",duration);
170     return 0;
171 }

 我运行C采用的是Ubuntu16 下的gcc编译器,运行结果截图如下:

多次运行结果差不多,基本每次都可以很接近最优解。而且发现C语言运行时间要远快于MATLAB实现(我记得MATLAB要用好几秒,这里就不贴MATLAB代码进行运行时间对比了),只需要耗时0.004秒左右。这里只讨论了基本的粒子群算法,后面一篇我还会对于粒子群的参数w进行详细的讨论,讨论不同的w参数的取法对于粒子群寻优能力的影响。

热爱编程,热爱机器学习! 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算法通过迭代更新粒子位置寻找最优解,旨在最小化网络损耗并提升供电可靠性。仿真结果显示优化后各项指标均有显著改善。