全局最优具体求解:
遗传算法求解过程:
全局最优框架总结:
代码实现:
函数极值问题-全局最优
```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, ¤t_best_individual, fun);
ChromDecoding(¤t_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;
}