遗传算法是比较经典的群体智能算法,属于随机搜索算法的一种。遗传算法属于全局性搜索算法,因为存在基因交叉互换和基因突变,因此比较不容易陷入局部最优。遗传算法的本质是随机生成一些合理的解,通过计算代入变量得出的值,优胜劣汰,保存优良解,淘汰不良解,再通过随机地两两交配互换对应变量的值以及改变某解中的某一变量来生成新的解向量,这些过程可以称之为适应度评估,交叉,突变。对于交配对象的分配,本算法使用轮盘赌选择法。
头文件如下
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
/*parameter*/
#define POPSIZE 100 /*population size*/
#define VARSNUM 3 /*variable number*/
#define PXOVER 0.7 /*probability of crossover*/
#define PMUTATION 0.07 /*probability of mutation*/
#define MAXGENS 500 /*max number of generations*/
#define TRUE 1
#define FALSE 0
/*global variable*/
int generation; /*current generatons number*/
int current_best; /*current best individual*/
double val_best; /*current best fitness*/
FILE *galog; /*output file*/
FILE *output;
struct popmem{
double gene[VARSNUM]; /*a array to store variables' value*/
double fitness; /*a value to refelct current variable's priority*/
double upboundary[VARSNUM]; /*range of variables*/
double lowboundary[VARSNUM];
double rfitness; /*relative fitness of current generation*/
double cfitness; /*for roulette wheel selection strategy*/
};
struct popmem population[POPSIZE+1]; /*current generation*/
struct popmem newpopulation[POPSIZE+1]; /*next generation*/
void Initial(void); /*initialize the group*/
void Evaluate(void); /*evalute the member's fitness*/
void RepBest(void); /*save the best member*/
void Select(void); /*select the members who take part in mating*/
void Crossover(void); /*the gene of those member crossover*/
void Mutation(void); /*the gene of those member mutate*/
void Survive(void); /*chose the members who survive*/
/*******************************/
double Randvar(double lb,double ub); /*generate a value between lb and ub randomly*/
void Report(void); /*print the results*/
void Xover(int one,int two); /*gene crossover*/
void Swap(double* gene1,double* gene2); /*swap the value between two genes*/
定义了一些常用的参数,比如交配概率,即被选择的个体参与交配的概率,一般取0.4-0.99;突变概率,即解的分量数值改变的概率,一般取0.001-0.1之间。以及定义了群体个体数,最大计算代数,变量维度。
定义了群体中个体的数据结构,包含基因(变量)的数组,上下界数组,适应度,以及用于选择交配个体的两个相对适应度。
定义了用到的函数,比如初始化、适应度评估、选择交配群体、交叉、基因突变等等。
①初始化函数
void Initial(void){
/*input the value range we need from the txt file.if it is not there,terminate the program*/
FILE *inputfile;
if((inputfile = fopen("gadata.txt","r")) == NULL){
fprintf(galog,"There is no data we need in the file!\n");
exit(1);
}
/*if there is a output data file generated last time,delete it!*/
remove("output.dat");
double lbound,ubound;
generation = 0;
for(int i = 0;i< VARSNUM;i++){
fscanf(inputfile,"%lf",&lbound);
fscanf(inputfile,"%lf",&ubound);
/*initialize the parameter*/
for(int j =0;j<POPSIZE;j++){
population[j].fitness = 0;
population[j].rfitness = 0;
population[j].cfitness = 0;
population[j].lowboundary[i] = lbound;
population[j].upboundary[i] = ubound;
population[j].gene[i] = Randvar(population[j].lowboundary[i],population[j].upboundary[i]);
}
}
fclose(inputfile);
}
先是使用fopen()函数读取txt文件中的上下界的数据,在利用srand()和rand()函数随机生成上下界范围内的初始解。之后对群体内的个体数据结构进行初始化,把上下界的数值赋值,计算适应度。
②评估函数
void Evaluate(void){
for(int i = 0;i<POPSIZE;i++){
double x[VARSNUM+1];
x[0] = 0;
for(int j = 0;j<VARSNUM;j++){
x[j+1] = population[i].gene[j];
}
/*if you change the function you want to optimize,you must change the code in this part */
population[i].fitness = x[1]*x[1]+x[2]*x[2]+x[3]*x[3];
if((output = fopen("output.dat","r")) == NULL) exit(1);
fprintf(output,"%d %d %6.9f %6.9f %6.9f %6.9f \n",generation,i+1,x[1],x[2],x[3],population[i].fitness);
}
fclose(output);
double fitness_sum = 0;
for(int j = 0;j<POPSIZE;j++){
fitness_sum += population[j].fitness;
}
/*to calculate the relative fitness and the cumulative fitness for every member in thre group*/
population[0].rfitness = population[0].fitness/fitness_sum;
population[0].cfitness = population[0].rfitness;
for(int k = 1;k<POPSIZE;k++){
population[k].rfitness = population[k].fitness/fitness_sum;
population[k].cfitness = population[k-1].cfitness + population[k].rfitness;
}
}
这里偷懒的方法,要修改适应度函数需要修改代码,不具有通用性,这里为了简单,因为原理弄清楚就行。rfitness为相对适应度,作为轮盘赌的百分比;cfitness是为了判断随机生成的数值落在哪个区间而设置,为了方便,无实际意义。
③保存最好的个体
void RepBest(void){
current_best = 0;
val_best = 0;
for(int i = 1;i<POPSIZE;i++){
if(population[i].fitness > population[current_best].fitness)
current_best = i;
}
val_best = population[current_best].fitness;
/*the best member is stored in the last of the array*/
population[POPSIZE] = population[current_best];
}
这个比较简单不详细说明。
④选择交配个体
void Select(void){
double RandNum[POPSIZE];
int SelectedMem[POPSIZE];
for(int i = 0;i<POPSIZE;i++){
RandNum[i] = Randvar(0.0,1.0);
for(int j = 0;j<POPSIZE;j++){
if(RandNum[i] <= population[j].cfitness){
SelectedMem[i] = j;
break;
}
}
}
/*the selected members to breed are store in the array newpopulation */
for(int j =0;j<POPSIZE;j++){
newpopulation[j] = population[SelectedMem[j]];
}
/*copy it because the newpopulation will be used immediately*/
for(int k =0;k<POPSIZE;k++){
population[k] = newpopulation[k];
}
}
这里是先计算出各个体的适应度之和,再分别计算它们的百分比,作为轮盘赌的百分比,然后利用rand()在[0,1]随机生成一个浮点数,看它落在哪个区间,哪个区间代表的个体就被挑出。
⑤交叉
void Crossover(void){
int flag,count;
double Randnum;
flag = 0;
count = 0;
for(int i =0;i<POPSIZE;i++){
Randnum = Randvar(0.0,1.0);
if(Randnum < PXOVER){
count++;
if(count%2 == 0){
Xover(flag,i);
}
else flag = i;
}
}
}
交叉的过程比较复杂,先解释两个函数,Xover实现两组基因的交叉互换,Swap实现两个浮点数的数值互换。
本函数先是按顺序分别生成随机数看是否小于PSOVER,相当于看概率事件是否发生。交配需要两个都被选中而且能交配的个体进行配对,因此如果最后能交配的个体是奇数个,最后一个个体保持原状。
⑥基因突变
void Mutation(void){
double Randnum;
for(int i = 0;i<POPSIZE;i++){
for(int j =0;j<VARSNUM;j++){
Randnum = Randvar(0,1.0);
if(Randnum < PMUTATION)
population[i].gene[j] = Randvar(population[i].lowboundary[j],population[i].upboundary[j]);
}
}
}
⑦保留精英
void Survive(void){
double best = population[POPSIZE].fitness;
double worst = population[0].fitness;;
int current_worst =0;
for(int i = 1;i<POPSIZE;i++){
if(population[i].fitness < worst){
worst = population[i].fitness;
current_worst = i;
}
}
if(worst < best) population[current_worst] = population[POPSIZE];
}
把上一代最好的个体与新一代最差的个体比较,如果上一代的个体比该个体优质,则替代之。
⑧主函数
void main(){
if((galog = fopen("galog.txt","r")) == NULL) exit(1);
fprintf(galog,"welcome to use this algorithm.\n");
srand(time(0));
Initial();
Evaluate();
RepBest();
while(generation < MAXGENS){
generation++;
Select();
Crossover();
Mutation();
Report();
Evaluate();
Survive();
RepBest();
}
fprintf(galog,"\n\n Simulation completed\n");
fprintf(galog,"\n Best Member : \n");
for(int i =0;i<VARSNUM;i++){
fprintf(galog,"\n x[%d] = %3.9f ",i,population[POPSIZE].gene[i]);
}
fprintf(galog,"\n Best Fitness = %3.9f ",population[POPSIZE].fitness);
fclose(galog);
printf("Success!\n");
}
有限次迭代后,把结果输出。
总结:这个算法是1995年提出,被用于许多多目标优化问题的求解,但本质来说是无聊的,因为本质是随机搜索,相当于做实验,而且过程是不可复制的。