油克小学期(六)全局最优搜索(2)函数极值求解-全局

全局最优具体求解:

 遗传算法求解过程:

全局最优框架总结:

代码实现:

函数极值问题-全局最优

```c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define Status int
#define TRUE 1
#define FALSE 0

#define PI 3.14159
//1.数学函数
double f(double x0, double x1)
{
    double y;
    y = sin(x0) + sin(x1);
    return y;
}


//通用函数
double fun(double *xs, double (f)())
{
    return f(xs[0], xs[1]);
}


/*2.问题表示和存储。定义染色体个体类型为INDIVIDUAL,包含染色体由二进制基因位0或1构成,并有成员chrom表示,
设置最长染色体长度为MAXCHROMLENGTH。针对多元函数(如变量个数不同)和求解精度(编码程度λ),需要设置染色体
的真实长度length。作为染色体个体,需要设置个体中的适应度值fitness。*/
#define MAXCHROMLENGTH 250                  //染色体最大长度
typedef struct INDIVIDUAL                   //定义染色体个体的结构体
{
    int chrom[MAXCHROMLENGTH];              //染色体的二进制表达形式
    int length;                             //染色体的实际长度
    double fitness;                         //染色体的适应值
} INDIVIDUAL;

/*3.单个变量编码和解码。根据式(6.1)和式(6.2)可以对任意给定以变量x进行二进制编码Coding和解码Decoding。
对变量x的编码和解码涉及到二进制编码code的长度lamda和区间[xmin, xmax]。以下代码的变量、编码/解码长度、最大值
和最小值用指针表示。*/

//3.1 Coding 对数据进行二进制编码
void Coding(int code[], int *lamda, double *x, double *xmin, double *xmax)
{               //变量编码
    //按区间[xmin, xmax]和二进制编码的长度lamda对数据x进行二进制编码
    int i, count;                                   //遍历变量
    double delta;                                   //精度δ
    for (i = 0; i < *lamda; i++)                    //默认每位均为0
        code[i] = 0;
    delta = (*xmax - *xmin) / (pow(2, *lamda) - 1); //计算精度
    count = (int)((*x - *xmin) / delta + 0.5);      //计算相对整数,即delta的个数
    i = 0;
    while(count)    {
        code[i++] = count % 2;                      //求余数0或1
        count = count / 2;                          //求商
    }
}

//3.2 Decoding 对二进制的编码进行解码进而转化为数据。
void Decoding(int code[], int *lamda, double *x, double *xmin, double *xmax)
{       //变量解码
    //按区间[xmin, xmax]和二进制编码的长度lamda对数据x进行二进制解码
    int i, pw = 1;
    double delta;                                       //精度δ
    delta = (*xmax - *xmin) / (pow(2, *lamda) - 1);     //计算精度
    *x = 0;                                             //默认相对整数的解码为0
    for (i = 0; i < *lamda; i++)    {                   //根据实际长度逐一解码
        *x = *x + code[i] * pw;                         //二进制码0或1乘以基数,计算相对数值
        pw = pw * 2;                                    //进位,乘以基数
    }
    *x = *x * delta + *xmin;                            //计算实际数值
}


//4.染色体个体的编码和解码
/*由于多元函数存在多个变量,因此需要统一由一个染色体个体表示。对于多元(num)的变量集合为xs。其对应的区间最小
集合为xmins、最大值集合为xmaxs、二进制编码的长度集合为lamdas。染色体个体的适应度值为数学函数fun的值。*/

//4.1.ChromCoding 对多变量进行统一的二进制染色体编码,即多变量形成一条染色体。
void ChromCoding(INDIVIDUAL *individual, int num, double *xs, int *lamdas, double *xmins,
double *xmaxs, double (*fun)())
{   //对染色体个体编码
    //个数为num的一组数据xs,根据各自对应的步长lamda和区间[xmin, xmax]
    //以及适应函数fun进行染色体个体的编码 individual

    int i, j, k, max_lamda = 0;
    int *code;                                          //二进制编码
    individual->fitness = fun(xs, f);                   //个体适应值为函数值
    individual->length = 0;                                 //个体individual实际长度为各变量对应长度之和
    for (i = 0; i < num; i++)   {
        individual->length += lamdas[i];                //每个变量编码长度均为lamdas[i]
        if(max_lamda < lamdas[i])                       //各变量对应的最大长度
            max_lamda = lamdas[i];
    }

    code = (int *)malloc(max_lamda * sizeof(int));      //分配二进制编码空间
    j = 0;                                              //个体编码的下标
    for (i = 0; i < num; i++)   {
        Coding(code, lamdas++, xs++, xmins++, xmaxs++); //对第i个变量进行编码
        for (k = 0; k < *(lamdas - 1); k++)
            individual->chrom[j] = code[k];             //将二进制编码code逐位放置于个体中
    }
    free(code);                                         //回收代码空间
}

//4.2. ChromDecoding 将一条染色体解码为多个变量。
void ChromDecoding(INDIVIDUAL *individual, int num, double *xs, int *lamdas, double *xmins,
double *xmaxs)
{   //染色体个体解码
    //染色体个体的编码individual根据个体num、各自对应的长度lamda和区间[xmin, xmax]
    //对个体进行解码,解码成一组数据xs
    int i, lamda = 0;
    for (i = 0; i < num; i++)   {           //逐个数据解码
        //第i个数据解码xs[i]
        Decoding(individual->chrom + lamda, lamdas++, xs++, xmins++, xmaxs++);
        lamda += *(lamdas - 1);             //下一个解码的长度
    }
}


/*5.初始化染色体种群及其适应值的评估。根据变量个数num和变量二进制编码长度集合lamdas将染色体种群初始化成
规模为popsize的种群population */

//5.1InitPoplation 将多个变量初始化为一个种群,表示多个可能解。
void InitPopulation(int popsize, INDIVIDUAL poplation[], int num, int *lamdas)
{       //初始化
    //根据种群规模popsize、变量个数num及每个变量的编码长度lamdas初始化种群poplation
    int i, j, chrom_length = 0;
    for (i = 0; i < num; i++)                                   //计算染色体个体的实际长度
        chrom_length += lamdas[i];
    srand((unsigned)time(NULL));                                //以当前时间为随机数种子
    for (i = 0; i < popsize; i++)   {                           //初始化每个个体
        poplation[i].length = chrom_length;                     //染色体个体的实际长度
        for (j = 0; j < chrom_length; j++)                      //染色体个体的每个基因位
            poplation[i].chrom[j] = (rand() % 10 < 5) ? 0 : 1;  //根据随机数置为0或1
    }
}

//5.2EvaluateIndividual 计算每条染色体的适应度函数值,即函数值
void EvaluateIndividual(INDIVIDUAL *individual, int num, int *lamdas, double *xmins,
double *xmaxs, double (*fun)())
{       //对染色体个体individual的适应度值进行评估计算
    double *xs;
    xs = (double *)malloc(num * sizeof(double));                //分配一组变量空间
    ChromDecoding(individual, num, xs, lamdas, xmins, xmaxs);   //将个体individual解码为一组变量xs

    individual->fitness = fun(xs, f);                           //适应度值为数学函数值
    free(xs);                                                   //回收一组变量
}

//5.3 EvaluatePopulation 计算种群中每条染色体的适应度函数值, 即种群中每个个体函数值
void EvaluatePopulation(int popsize, INDIVIDUAL poplation[], int num,
int *lamdas, double *xmins, double *xmaxs, double (*fun)() )
{       //对种群population中的每个个体的适应度值进行评估计算
    int i;
    for (i = 0; i < popsize; i++)                   //对种群中每个个体适应度值的评估
        EvaluateIndividual(poplation + i, num, lamdas, xmins, xmaxs, fun);
                                                    //评估第i个个体
}


//6.染色体种群最优个体和当前最优个体
/*根据染色体种群规模popsize, 从染色体种群population中获取最优染色体个体best_individual */
//6.1 GetBestIndividual 获取种群中最优的染色体个体
void GetBestIndividual(int popsize, INDIVIDUAL population[],
INDIVIDUAL *best_individual)
{       //种群population中适应度值最大的个体bestindividual
    int i;
    *best_individual = population[0];                           //默认最优个体
    for (i = 1; i < popsize; i++)                               //依次比较
        if(population[i].fitness > best_individual->fitness)    //当前第i个个体是否更优
            *best_individual = population[i];                   //更新最优个体
}

//6.2 UpdateCurrentIndividual 在每次遗传进化过程中更新当前最优个体。
Status UpdateCrrentIndividual(INDIVIDUAL *best_individual,
INDIVIDUAL *current_best_individual)
{   //更新当前最优个体
    //在进化过程中迄今最优个体 current_best_individual 与种群中最优个体best_individual的比较
    Status flag = FALSE;                                            //比较结果的默认值
    if(best_individual->fitness > current_best_individual->fitness) //种群个体更优
    {
        *current_best_individual = *best_individual;                //更新当前最优个体
        flag = TRUE;                                                //更新比较结果
    }
    return flag;                                                    //返回是否更新当前最优个体
}


//7.遗传算子

/*7.1 SelectOperator(选择算子)根据种群染色体适应值的大小确定每条染色体的概率分布。
采用轮盘赌选择法选取染色体个体重新构成种群规模为popsize的种群population */
void SelectOperator(int popsize, INDIVIDUAL population[])
{
    int i, j;
    double p, sum, minfitness;                      //p存放随机概率,sum存放个体适应率和累计适应率
    double *porp;                                   //当代种群染色体个体的适应率
    INDIVIDUAL *newpopulation;                      //新种群
    porp = (double *)malloc(popsize * sizeof(double));      //分配概率空间和新种群空间
    newpopulation = (INDIVIDUAL *)malloc(popsize * sizeof(INDIVIDUAL));
    minfitness = population[0].fitness;             //种群中的最小适应度值
    for (i = 1; i < popsize; i++)   {               //更新种群中的最小适应度值
        if(minfitness > population[i].fitness)
            minfitness = population[i].fitness;
    }
    porp[0] = population[0].fitness - minfitness;   //相对偏移最小值,考虑有些函数值为负数的情况
    //对下面的这条语句表示疑问
    sum = population[0].fitness - minfitness;       //相对偏移值之和

    for (i = 1; i < popsize; i++)   {               //遍历每个个体
        sum += population[i].fitness - minfitness;  //sum存放种群适应值偏移后的总和
        porp[i] = porp[i - 1] + population[i].fitness - minfitness; //当前项以前所有适应度偏移值之和
    }
    for (i = 0; i < popsize; i++)                   //当前项偏移前的总和
        porp[i] = porp[i] / sum;

    for (i = 0; i < popsize; i++)   {               //利用轮盘赌选择法选择popsize个个体
        p = rand() % 10000 / 10000.0;               //得到万分位小数的小数,即随机概率
        for (j = 0; porp[j] < p; j++)               //转动轮盘
            newpopulation[i] = population[j];       //选出个体暂时存放于newpopulation中
    }
    for (i = 0; i < popsize; i++)
        population[i] = newpopulation[i];           //更新种群population
    free(porp);                                     //回收概率空间
    free(newpopulation);                            //回收种群空间
}

/*7.2 CrossoverOperator(交叉算子)根据交叉概率pc依次选取种群中染色体个体及其随机其他个体,
并随机确定两条染色体的基因位,将该位后的所有基因依次交换,进而产生两个新个体,并最终生成新种群。*/
void CrossoverOperator(int popsize, INDIVIDUAL population[], double pc)
{   //交叉算子
    //根据交叉概率pc对种群population中的个体进行随机选取,
    //并随机确定交叉点进行交叉生成新个体的同时进一步组建成新种群
    int i, j, *index, point, temp;
    double p;
    int chromlength = population[0].length;         //染色体个体的长度
    index = (int *)malloc(popsize * sizeof(int));   //分配一组索引空间作为种群个体索引
    for (i = 0; i < popsize; i++)                   //初始化index为个体下标
        index[i] = i;
    for (i = 0; i < popsize; i++)   {               //种群内随机两两交换索引,打乱种群顺序
        j = rand() % (popsize - i);                 //随机产生下标
        temp = index[i];                            //交换索引,索引发生变化
        index[i] = index[j + i];
        index[j + i] = temp;
    }
    for (i = 0; i < popsize - 1; i+=2)  {           //index在逻辑上连接相邻两条染色体
        p = rand() % 1000 / 1000.0;                 //选取个体概率
        if(p < pc)                                  //第index[i]个个体被选
        {
            point = rand() % (chromlength - 1) + 1; //随机确定交叉点的位置
            for (j = 0; j < chromlength; j++)       //在交叉点后进行基因交换
            {                                       //在第index[i]、index[i+1]个体的point后进行基因交换
                temp = population[index[i]].chrom[j];
                population[index[i]].chrom[j] = population[index[i + 1]].chrom[j];
                population[index[i + 1]].chrom[j] = temp;
            }
        }
    }
    free(index);                                    //回收所有空间
}

/*7.3 MutateOperator(变异算子)根据变异概率pm对染色体种群中所有染色体的所有基因依次进行变异,
产生新染色体种群。*/
void MutateOperator(int popsize, INDIVIDUAL population[], double pm)    //变异算子
{   //根据变异概率pm对种群population中所有基因位0或1变异生成新种群
    int i, j, chromlength;
    double p;
    chromlength = population[0].length;             //染色体个体的长度
    for (i = 0; i < popsize; i++)   {               //所有染色体个体
        for (j = 0; j < chromlength; j++)           //所有基因位
        {
            p = rand() % 1000 / 1000.0;             //每个基因位的随机概率
            if(p < pm)                              //决定是否变异
                population[i].chrom[j] = (population[i].chrom[j] == 0) ? 1 : 0;
        }                                           //0 变成 1 或者 1 变成 0
    }
}


//8 新生代生成与进化。

/*GenerateNextPopulation 在选择算子、交叉算子和变异算子基础上,根据交叉概率pc、变异概率pm、染色体种群规模
popsize和种群population生成新一代种群。 */
void GenerateNextPopulation(int popsize, INDIVIDUAL population[], double pc, double pm)
{       //生成新一代种群
    //根据交叉概率pc和变异概率pm,通过3个遗传算子由种群population生成新种群
    SelectOperator(popsize, population);            //通过选择算子生成种群
    CrossoverOperator(popsize, population, pc);     //通过交叉算子生成种群
    MutateOperator(popsize, population, pm);        //通过变异算子生成种群
}

/*根据遗传算法的基本要求,在变量编码、染色体群体随机初始化、染色体群体适应值评估、遗传算子和新种群生成等基础上,
实现对全局搜索函数最优问题的求解。问题求解结束条件是连续进化若干次而当前最优个体保持不变,即最优解已经稳定。
综合上述各函数具体实现如下: */
void GA(int popsize, int num, int *lamdas, double *xmins, double *xmaxs, double pc, double pm,
int generation, INDIVIDUAL *current_best_individual, double (*fun)())
{       //进化
    //给定数据个数num、一组二进制编码长度lamda、一组数据区间[xmin, xmax],
    //交叉概率pc,变异概率pm,问题求解过程的连续精度不变
    //进化代数generation,待求解函数fun,求解结果当前最优个体current_best_individual

    int i = 0;
    INDIVIDUAL *population, best_individual, old_best_individual;
    population = (INDIVIDUAL *)malloc(popsize * sizeof(INDIVIDUAL));

    InitPopulation(popsize, population, num, lamdas);

    EvaluatePopulation(popsize, population, num, lamdas, xmins, xmaxs, fun);
    best_individual = population[0];
    for (i = 1; i < popsize; i++)   {
        if(population[i].fitness > best_individual.fitness)
            best_individual = population[i];
    }
    *current_best_individual = best_individual;
    old_best_individual = best_individual;
    i = 0;
    while(i < generation)   {
        GenerateNextPopulation(popsize, population, pc, pm);

        EvaluatePopulation(popsize, population, num, lamdas, xmins, xmaxs, fun);

        GetBestIndividual(popsize, population, &best_individual);

        UpdateCrrentIndividual(&best_individual, current_best_individual);

        if(fabs(current_best_individual->fitness - old_best_individual.fitness) < 1e-5) {
            i++;
        } else{
            old_best_individual = *current_best_individual;
            i = 0;
        }
    }
    free(population);
}

int main(int argc, char* argv[])
{
    int num = 2, lamdas[] = {20, 20};
    double xmins[] = {0, 0}, xmaxs[] = {720, 720};
    double xs[2];
    int i, j, k, popsize = 48;
    double pc = 0.8;
    double pm = 0.05;
    int generation = 50000;
    int count = 10;

    INDIVIDUAL current_best_individual;

    for (i = 0; i < count; i++)
    {
        GA(popsize, num, lamdas, xmins, xmaxs, pc, pm, generation, &current_best_individual, fun);

        ChromDecoding(&current_best_individual, num, xs, lamdas, xmins, xmaxs);

        printf("Result:");
        for (j = 0; j < num; j++)   {
            printf("x%-d = %-10lf    ", j + 1, xs[j]);
        }

        printf("y = %lf\n", current_best_individual.fitness);

        for (j = 0; j < 32767; j++) {
            for (k = 0; k < 32767; k++);
        }
    }

    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北城学神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值