【人工智能】遗传算法(GA)入门—以求解一元函数最大值的优化问题为例

前言

遗传算法有很多优化和变形,本文将从最基本的遗传算法出发,以一个简单的优化问题作为例子来说明遗传算法的代码实现,比较适合已经学了相关理论知识的初学者进行实践学习。

一、问题描述

用GA求解一元函数的最大值:

f(x) = x sin(10πx) + 2.0, x∈[-1,2]

二、编码

变量x可以视为遗传算法的表现型形式,我们采用二进制编码形式。如果设定求解精度要精确到6位小数,由于区间长度为3,故将区间分为3*10^6等份,因为:

2097152 = 2^21 < 3*10^6 < 2^22 = 4194304

所以编码的二进制串长至少为22位。

我们采用二进制编码,将一个二进制串与区间[Left,Right]间对应的实数值建立对应:

假设二进制串b的十进制值为x’,则:

x = (Right-Left)*x'/(2^22-1.0)+Left;

三、产生初始种群

我们通过随机产生一些个体(即随机产生一些二进制串),来初始化我们的种群。

/*****function:generation the first popolation初始化群体*****/
void GenerateInitialPopulation (void)
{ 
    int i,j;
    srand((unsigned)(time(NULL)));
    for (i=0;i<PopSize;i++)
    {
        for (j=0;j<CHROMLENGTH;j++)
        {
            population [i].chrom[j]=(random(10)<5)?'0':'1';
        }
        population [i].chrom[CHROMLENGTH]='\0';
    }
}

四、对种群进行评估

/****function: evaluate population according to certain formula衡量群体*****/
void EvaluatePopulation (void)
{  
    CalculateObjectValue ();
    CalculateFitnessValue ();
    FindBestAndWorstIndividual ();
}

1.计算目标函数值

/*****function: to decode a binary chromosome into a decimal integer*****/
long DecodeChromosome (char *string,int point,int length)
{ 
    int i;
    long decimal=0L;
    char *pointer;
    for (i=0,pointer=string+point;i<length;i++,pointer++)
    { 
        decimal+=(*pointer-'0')<<(length-1-i);
    }
    return (decimal);
}

/***** function:to calculate object value f(x) = x sin(10πx) + 2.0 *****/
void CalculateObjectValue (void)
{ 
    int i;
    long temp;
    double x;
    /*** rosenbrock function***/
    for (i=0;i<PopSize;i++)
    {       
        temp=DecodeChromosome (population[i].chrom,0,CHROMLENGTH);
        x=(Right-Left)*temp/(pow(2,CHROMLENGTH)-1.0)+Left;
            population[i].value=x*sin(10*PI*x)+2.0;//函数值
        population[i].x=x;//对应的自变量
    }
}

2.计算适应值

在本例中,目标函数在定义域内大于0,而且求函数的最大值,所以我们直接引用目标函数作为适应度函数。

/******function: to calculate fitness value *******/
void CalculateFitnessValue (void)
{ 
    int i;
    for(i=0;i<PopSize;i++)
    {
        population[i].fitness=population[i].value;
    }
}

3.找出局部最优个体与局部最差个体,并更新全局最优个体

/*****function to find out the best individual so far current generation*****/
void FindBestAndWorstIndividual (void)
{ 
    int i;
    double sum=0.0;
    /*** find out the best and worst individual of this generation***/
    bestindividual=population[0];
    worstindividual=population[0];
    for(i=1;i<PopSize;i++)
    {
        if (population[i].fitness>bestindividual.fitness)
        {bestindividual=population[i];best_index=i;}
        else if(population[i].fitness<worstindividual.fitness)
        {worstindividual=population[i];worst_index=i;}
        sum+=population[i].fitness;
    }
    /***find out the best individual so far***/
    if (generation==0){ currentbest=bestindividual;}
    else 
    { 
        if(bestindividual.fitness>currentbest.fitness) {currentbest=bestindividual;}
    }
}

五、遗传操作

我们按照轮盘赌的方式来选择子个体,对选出来的个体,两两进行交叉操作,随机选择一个交叉点。

交叉之后进行变异操作,在二进制编码中,变异体现在位翻转上。

/*****function: generate the next generation产生新种群*****/
void GenerateNextPopulation (void)
{  
    SelectionOperator ();
    CrossoverOperator ();
    MutationOperator ();
}


/*****function: to reproduce a chromosome by roulette wheel seclection*****/
void SelectionOperator (void)
{ 
    int i,j,index;
    double p,sum=0.0;
    double cfitness[POPSIZE];  /*cumulative fitness value*/
    struct individual newpopulation[POPSIZE];
    /***calculate relative fitness***/
    for(i=0;i<PopSize;i++) {sum+=population[i].fitness;}
    for(i=0;i<PopSize;i++){cfitness[i]=population[i].fitness/sum;}
    /***calculate cumulative fitness***/
    for(i=1;i<PopSize;i++){cfitness[i]=cfitness[i-1]+cfitness[i];}
    /***selection operation***/
    for(i=0;i<PopSize;i++)
    {
        p=random(1000)/1000.0;
        index=0;
        while(p>cfitness[index]){index++;}
        newpopulation[i]=population[index];
    }
    for(i=0;i<PopSize;i++){population[i]=newpopulation[i];}
}

/*****function:crossover two chromosome by means of one-point crossover*****/
void CrossoverOperator (void)
{ 
    int i,j;
    int index[POPSIZE];
    int point,temp;
    double p;
    char ch;
    /***make a pair of individual randomly***/
    for(i=0;i<PopSize;i++){index[i]=i;}
    for(i=0;i<PopSize;i++)
    {
        point=random(PopSize-i);
        temp=index[i];
        index[i]=index[point+i];
        index[point+i]=temp;
    }
    /***one-point crossover operation***/
    for(i=0;i<PopSize-1;i+=2)
    {
        p=random(1000)/1000.0;
        if(p<Pc)
        { 
            point=random(CHROMLENGTH-1)+1;
            for(j=point;j<CHROMLENGTH;j++)
            { 
                ch=population[index[i]].chrom[j];
                population[index[i]].chrom[j]=population[index[i+1]].chrom[j];
                population[index[i+1]].chrom[j]=ch;
            }
        }
    }
}

/*****function: mutation of a chromosome*****/
void MutationOperator (void)
{ 
    int i,j;
    double p;
    /*** bit mutation***/
    for(i=0;i<PopSize;i++)
    {
        for(j=0;j<CHROMLENGTH;j++)
        {
            p=random(1000)/1000.0;
            if(p<Pm){population[i].chrom[j]=(population[i].chrom[j]=='0')?'1':'0';}
        }
    }
}

六、进化

/*****function:to perform evolution operation based on elitise mode. elitist model is to replace the worst individual of this generation by the current best one保留最优个体*****/
void PerformEvolution (void)
{ 
    if(bestindividual.fitness>currentbest.fitness){currentbest=population[best_index];}
    else{population[worst_index]=currentbest;}
}

七、模拟结果

我们设定种群大小为80,交叉概率为Pc=0.6,变异概率为Pm=0.001,按照标准的遗传算法SGA,在运行到200代时获得的最佳个体为:

x=1.850549
f(x)=3.850275 
chromosome=1111001100111111001011

这个个体对应的解与微分方程预计的最优解的情况吻合。

附录

完整代码

# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <math.h>
/****** the definition of constant******/
# define PI 3.14159  
# define POPSIZE 80
/****** the definition of user data*****/
# define LEFT -1
# define RIGHT 2
# define CHROMLENGTH 22

# define random(x) rand()%x

  const int MaxGeneration=200;
  const double Pc=0.6;
  const double Pm=0.001;

/***** the definition of data structure*****/
  struct individual
  { 
    char chrom[CHROMLENGTH+1];//基因 
    double x;   //自变量 
    double value;//目标函数值
    double fitness;//适应度
  };

/***** the definition of global variables*****/
  int generation;
  int best_index;
  int worst_index;
  struct individual bestindividual;     //局部最优个体
  struct individual worstindividual;    //局部最差个体
  struct individual currentbest;        //全局最优个体 
  struct individual population[POPSIZE];//种群 

/*****declaration of prototype 原型声明*****/
  void GenerateInitialPopulation (void);    //初始化种群 
  void GenerateNextPopulation (void);       //产生下一代种群 
  void EvaluatePopulation (void);           //评估 
  long DecodeChromosome (char *,int,int);   //对基因进行解码 
  void CalculateObjectValue (void);         //计算目标函数值 
  void CalculateFitnessValue (void);        //计算适应值 
  void FindBestAndWorstIndividual (void);   //寻找最优及最差个体 
  void PerformEvolution (void);             //进化 
  void SelectionOperator (void);            //选择 
  void CrossoverOperator (void);            //交叉 
  void MutationOperator (void);             //变异 
  void OutputTextReport (void);


/***** main program*****/
int main (void)
{ 
    generation=0;
    GenerateInitialPopulation ();       //调用初始群体函数 
    EvaluatePopulation ();          //第一次评估 
    while (generation<MaxGeneration)    //迭代一定代数 
    { 
        generation++;
        GenerateNextPopulation ();      //根据评估的结果来产生下一代 
        EvaluatePopulation ();          //对新一代种群进行评估 
        PerformEvolution ();            //进化 
        OutputTextReport ();
    }
    return 0;
}

/*****function:generation the first popolation初始化群体*****/
void GenerateInitialPopulation (void)
{ 
    int i,j;
    srand((unsigned)(time(NULL)));
    for (i=0;i<POPSIZE;i++)
    {
        for (j=0;j<CHROMLENGTH;j++)
        {
            population [i].chrom[j]=(random(10)<5)?'0':'1';
        }
        population [i].chrom[CHROMLENGTH]='\0';
    }
}

/*****function: generate the next generation产生新种群*****/
void GenerateNextPopulation (void)
{  
    SelectionOperator ();
    CrossoverOperator ();
    MutationOperator ();
}

/****function: evaluate population according to certain formula衡量群体*****/
void EvaluatePopulation (void)
{  
    CalculateObjectValue ();
    CalculateFitnessValue ();
    FindBestAndWorstIndividual ();
}

/*****function: to decode a binary chromosome into a decimal integer*****/
long DecodeChromosome (char *string,int point,int length)
{ 
    int i;
    long decimal=0L;
    char *pointer;
    for (i=0,pointer=string+point;i<length;i++,pointer++)
    { 
        decimal+=(*pointer-'0')<<(length-1-i);
    }
    return (decimal);
}

/***** function:to calculate object value f(x) = x sin(10πx) + 2.0 *****/
void CalculateObjectValue (void)
{ 
    int i;
    long temp;
    double x;
    /*** rosenbrock function***/
    for (i=0;i<POPSIZE;i++)
    {       
        temp=DecodeChromosome (population[i].chrom,0,CHROMLENGTH);
        x=(RIGHT-LEFT)*temp/(pow(2,CHROMLENGTH)-1.0)+LEFT;
        population[i].value=x*sin(10*PI*x)+2.0;
        population[i].x=x;
    }
}

/******function: to calculate fitness value *******/
void CalculateFitnessValue (void)
{ 
    int i;
    for(i=0;i<POPSIZE;i++)
    {
        population[i].fitness=population[i].value;
    }
}

/*****function to find out the best individual so far current generation*****/
void FindBestAndWorstIndividual (void)
{ 
    int i;
    double sum=0.0;
    /*** find out the best and worst individual of this generation***/
    bestindividual=population[0];
    worstindividual=population[0];
    for(i=1;i<POPSIZE;i++)
    {
        if (population[i].fitness>bestindividual.fitness)
        {bestindividual=population[i];best_index=i;}
        else if(population[i].fitness<worstindividual.fitness)
        {worstindividual=population[i];worst_index=i;}
        sum+=population[i].fitness;
    }
    /***find out the best individual so far***/
    if (generation==0){ currentbest=bestindividual;}
    else 
    { 
        if(bestindividual.fitness>currentbest.fitness) {currentbest=bestindividual;}
    }
}

/*****function:to perform evolution operation based on elitise mode. elitist model is to replace the worst individual of this generation by the current best one保留最优个体*****/
void PerformEvolution (void)
{ 
    if(bestindividual.fitness>currentbest.fitness){currentbest=population[best_index];}
    else{population[worst_index]=currentbest;}
}

/*****function: to reproduce a chromosome by roulette wheel seclection*****/
void SelectionOperator (void)
{ 
    int i,j,index;
    double p,sum=0.0;
    double cfitness[POPSIZE];  /*cumulative fitness value*/
    struct individual newpopulation[POPSIZE];
    /***calculate relative fitness***/
    for(i=0;i<POPSIZE;i++) {sum+=population[i].fitness;}
    for(i=0;i<POPSIZE;i++){cfitness[i]=population[i].fitness/sum;}
    /***calculate cumulative fitness***/
    for(i=1;i<POPSIZE;i++){cfitness[i]=cfitness[i-1]+cfitness[i];}
    /***selection operation***/
    for(i=0;i<POPSIZE;i++)
    {
        p=random(1000)/1000.0;
        index=0;
        while(p>cfitness[index]){index++;}
        newpopulation[i]=population[index];
    }
    for(i=0;i<POPSIZE;i++){population[i]=newpopulation[i];}
}

/*****function:crossover two chromosome by means of one-point crossover*****/
void CrossoverOperator (void)
{ 
    int i,j;
    int index[POPSIZE];
    int point,temp;
    double p;
    char ch;
    /***make a pair of individual randomly***/
    for(i=0;i<POPSIZE;i++){index[i]=i;}
    for(i=0;i<POPSIZE;i++)
    {
        point=random(POPSIZE-i);
        temp=index[i];
        index[i]=index[point+i];
        index[point+i]=temp;
    }
    /***one-point crossover operation***/
    for(i=0;i<POPSIZE-1;i+=2)
    {
        p=random(1000)/1000.0;
        if(p<Pc)
        { 
            point=random(CHROMLENGTH-1)+1;
            for(j=point;j<CHROMLENGTH;j++)
            { 
                ch=population[index[i]].chrom[j];
                population[index[i]].chrom[j]=population[index[i+1]].chrom[j];
                population[index[i+1]].chrom[j]=ch;
            }
        }
    }
}

/*****function: mutation of a chromosome*****/
void MutationOperator (void)
{ 
    int i,j;
    double p;
    /*** bit mutation***/
    for(i=0;i<POPSIZE;i++)
    {
        for(j=0;j<CHROMLENGTH;j++)
        {
            p=random(1000)/1000.0;
            if(p<Pm){population[i].chrom[j]=(population[i].chrom[j]=='0')?'1':'0';}
        }
    }
}

/*****function: output the results of current population*****/
void OutputTextReport (void)
{ 
    int i;
    double sum;
    double average;   /***average of population object value***/
    /*** calculate average object value***/
    sum=0.0;
    for(i=0;i<POPSIZE;i++){sum+=population[i].value;}
    average=sum/POPSIZE;
    /***print results of this population ***/
    printf("gen=%d, avg=%f, x=%f, best=%f ",generation,average,currentbest.x,currentbest.value);
    printf("chromosome=");
    for(i=0;i<CHROMLENGTH;i++){printf("%c",currentbest.chrom[i]);}
    printf("\n");
}
    原文作者:遗传算法
    原文地址: https://blog.csdn.net/jiange_zh/article/details/49719549
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞