西电人工智能大作业(遗传算法求函数最大值)
一、问题描述
二、算法分析
step1:确定种群规模N,交叉概率pc,变异概率pm。随机生成N个个体作为初始种群P。
step2:计算种群P中每个个体的适应度值。
step3:
1)从P中依据轮盘赌选择出N/2对父代;
2)对选择的N/2对父代,依概率pc进行交叉,生成的子代个体记为 P_s;
3)对集合O1中的个体依概率pm进行变异,生成的子代个体记为P_m;
4))从P、P_s、P_m中依据l轮盘赌选出N个个体组成下一代种群P。
step4:更新最大值,重复step2。
三、具体实现
1.种群编码实现
求maxf(x),其中x的取值范围是[a,b],精度要求不小于10^(-k),那么二进制位数m应该满足pow(2,m-1) - 1 < (b - a) < pow(2,m)
因此根据以上公式可以求出x1和x2的编码长度为len1 = 18,len2 = 15,总编码长度为len = 33。
#define x1min -3.0
#define x1max 12.1
#define x2min 4.1
#define x2max 5.8
#define len1 18
#define len2 15
#define len 33
typedef struct //个体结构体
{
char code[len+1];
double x1;
double x2;
double fitness;
double fit_p;
double fit_p_c;
}Individual;
//种群初始化
void InitPopu(Individual *P,int N)
{
int i,k,n;
for(i = 0;i < N;i ++)
{
for(k = 0;k < len;k ++)
{
n = rand()%100;
if(n<50)
P[i].code[k] = '0';
else
P[i].code[k] = '1';
}
P[i].code[len] = '\0';
}
}
2.种群解码实现
容易想到解码的方法可以采用比值的映射。
即:
(x - 0)/ (pow(2,m)-1-0) = (y - a) / (b - a)
所以y = a + (b - a) * x / (pow(2,m) - 1)
注意这里需要将二进制字符串转为十进制整型。
代码实现如下:
long long int binaryToDecimal(char *binaryString)
{
int i;
long long int decimal = 0;
int length = strlen(binaryString);
for (i = 0; i < length; i++)
if (binaryString[i] == '1')
decimal += 1 << (length - i - 1);
return decimal;
}
void decode(Individual *P,int N)
{
int i,j;
char code1[len1+1];
char code2[len2+1];
for(i = 0;i < N;i ++)
{
for(j = 0;j < len1;j ++)
code1[j] = P[i].code[j];
code1[len1] = '\0';
for(j = 0;j < len2;j ++)
code2[j] = P[i].code[j+len1];
code2[len2] = '\0';
long long int code1num = pow(2,len1);
long long int code2num = pow(2,len2);
long long int num1 = binaryToDecimal(code1);
long long int num2 = binaryToDecimal(code2);
P[i].x1 = x1min + (x1max - x1min) * num1 / (code1num - 1);
P[i].x2 = x2min + (x2max - x2min) * num2 / (code2num - 1);
}
}
3.适应度计算实现
非常简单,直接上代码:
void CulFitness(Individual *P,int N)
{
int i;
for(i = 0;i < N;i ++)
P[i].fitness = 21.5 + P[i].x1 * sin(4 * pi * P[i].x1) + P[i].x2 * sin(20 * pi * P[i].x2);
}
4.轮盘赌实现
思路:
为群体中每个个体指定饼图中一个小块。块的大小与个体的适应度成比例,适应度愈高,它在饼图中对应的小块所占面积也愈大。为了选取一个个体,要做的就是旋转这个轮子,直到轮盘停止时,看指针停止在
哪一块上,就选中与它对应的那个个体。
具体方案如图所示:
代码实现如下:
void Roulette(Individual *P,Individual *P_t,int N,int M)
{
//计算P_t中N个个体适应度比值以及适应度比值前缀和
int i,j;
double sum = 0;
for(i = 0;i < N;i ++)
{
sum += P_t[i].fitness;
P_t[i].fit_p_c = sum;
}
for(i = 0;i < N;i ++)
{
P_t[i].fit_p = P_t[i].fitness / sum;
P_t[i].fit_p_c /= sum;
}
double n;
//从P_t中选出M个个体,存入P中
for(i = 0;i < M;i ++)
{
n = (double)rand() / RAND_MAX;
for(j = 0;j < N;j ++)
{
if(n<P[j].fit_p_c)
{
memcpy(&P[i],&P_t[j],sizeof(P[j]));
break;
}
}
}
}