CHEETAH.W

静心积累

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在书中的第11章遗传算法与进化计算(《智能控制与智能系统》第12节)中,介绍的进化计算的基础知识。进化计算分为三个方面:遗传算法(GA)、进化策略(ES)和进化规划(EP)。因此本文将提到的遗传算法就是可能影响最大的进化算法之一。

本文主要依据VC++程序设计的手段实现了遗传算法的基本算法。下面会对照《遗传算法及其应用》一书进行说明。

首先,遗传算法是模拟生物在自然环境中的遗传和进化过程而形成的一种自适应全局概率搜索算法。在书中(P9),对一个二元函数求最大值问题进行遗传算法的手工模拟计算,已经把遗传算法的基本方法模拟出来。主要包括:
1.个体编码
2.初始群体的产生
3.适应度计算
4.选择运算
5.交叉运算
6.变异运算 

在书中第二章结尾,针对另一个最值问题:Rosenbrock函数的全局最大值进行基本遗传算法求解。附整个工程。

题目:f(x1,x2)=100*(x1^2-x2)^2+(1-x1)^2
求最大值。 s.t. -2.048 ≤ xi ≤ 2.048 (i=1,2)

vs2008工程:Sample_of_Genetic_Algorithm.zip

core.h中声明了遗传算法功能类

重点是Decode(解码)E(适应度)Fi(比例选择)Fc(单点交叉)Fh(变异)几个函数

// core.h
/////////////////////////
// 遗传算法功能类
class GA{
public:
bool isrun; // 运行使能控制

int M; // 群体大小
int T; // 终止代数
double Pc; // 交叉概率
double Pm; // 变异概率
vector< vector<char> > P; // 当前种群
vector<double> f; // 对于当前种群的适应度
vector<double> t_c; // 测试用交叉列表记录
vector<double> t_h; // 测试用变异列表记录
list<double> f_max_his; // 记录进化趋势 适应度最大值
list<double> f_avr_his; // 记录进化趋势 适应度平均值
list<double> f_min_his; // 记录进化趋势 适应度最小值

double sum_f;
double f_max;
double f_min;
double f_avr;

int t; // 第t代

vector
<char> Encode(double in_); // 编码当前种群
double Decode(vector<char> in_, int pos); // 解码当前种群
void E(); // 这个函数按照当前种群P更新f,即适应度
void Fi(); // 比例选择算子,这个函数可以更新当前P种群
void Fc(); // 单点交叉算子,
void Fh(); // 变异算子
void InitP(int in_M, int in_T, double pc, double pm); // 初始化种群
double GetRand(double min,double max,int part); // 得到随机数

vector
<char> Get20Bin(); // 得到随机数

void printP(int level_); // 输出种群
void getStrP(int index, int pos_, wchar_t* out_); // 得到P的字符串表示
void writeHis();
};

当前种群P存放编码后的二进制数据(0101000101),所以只需要写解码函数Decode,不需要写编码Encode函数。

/*
* 解码当前种群
*/
double GA::Decode(vector<char> in_, int pos){
double v = .004;
int n;
double out_ = 0;
if(pos == 0){
for(n=9;n>=0;n--){
if(in_[n]==1) out_ += v;
v
*= 2;
}
}
else{
for(n=19;n>=10;n--){
if(in_[n]==1) out_ += v;
v
*= 2;
}
}
out_
-= 2.048;
return out_;
}

解码方法由编码方法决定,这里所采用的方法是将定义域-2.048 ≤ xi ≤ 2.048 (i=1,2)离散化为1023个均等的区域,共1024个不同的点,这样点间隔为0.004,也就是极限的结果精确程度。由于1024个离散值可用十位2进制编码表示,那么长度为20位的二进制编码就能表示x1、x2两个自变量。

/*
* 更新f,计算适应度
*/
void GA::E(){
double x1 = 0;
double x2 = 0;
double tempf = 0;
sum_f
= 0;
f_max
= (numeric_limits<double>::min)(); // double中的最小值
f_min = (numeric_limits<double>::max)();
vector
< vector<char> >::iterator itP = P.begin();
vector
< double >::iterator itf = f.begin();
while(itP != P.end()){
x1
= Decode(*itP,0);
x2
= Decode(*itP,1);
*itf = 100 * powl((x1*x1 - x2),2) + powl((1-x1),2);
if(f_max<*itf)f_max = *itf; // 更新最大值
if(f_min>*itf)f_min = *itf; // 更新最小值
sum_f += *itf;
itf
++;
itP
++;
}
f_avr
= sum_f / (double)M;
}

本题由于函数值域恒为正值,并且题目要求最大值,题目函数与允许的适应度函数正相关,因此直接使用题目函数作为适应度函数即可。另外顺便求出此代总体的特征值:最大值f_max,最小值f_min,平均值f_avr,适应度总和sum_f。

/*
* 比例选择,更新P
*/
void GA::Fi(){
vector
< vector<char> > res_(M);
vector
< double > f_integral(M);
vector
< vector<char> >::iterator itP = P.begin();
vector
< double >::iterator itf = f.begin();
vector
< double >::iterator itf_i = f_integral.begin();

double r;
int n,m;

// 计算积分
*itf_i = *itf;
for(itf_i++,itf++;itf!=f.end();itf++,itf_i++){
*itf_i = *(itf_i-1) + *itf;
}

res_.clear();
for(m=0;m<M;m++){
r = GetRand(0,f_integral[79],1024);
for(n=0;n<M;n++){
if(r<f_integral[n])
break;// 此时的n被选中
}
res_.push_back(P[n]);
}
P
= res_;
}

比例选择函数设计时,用到随机数,用到求中间变量积分(累加)数组的思想f_integral。循环判断选择……

/*
* 单点交叉算子
*/
void GA::Fc(){
double r;
// 两两配对 0-1 2-3 4-5 ... 78-79(M-2 ~ M-1)
for(int n=0;n<M;n+=2){
r
= GetRand(0,1,100);
if(r>Pc){
t_c[n]
= r;
t_c[n
+1] = r;
continue;
}
t_c[n]
= r;
t_c[n
+1] = r;
int pos = (int)GetRand(0,19,19);
Fc_swap(P[n],P[n
+1],pos);
}
}

此题目用到最简单的单点交叉。两两配对,随机选择一个位置,然后,交叉……

/*
* 变异算子
*/
void GA::Fh(){
double r;
for(int n=0;n<M;n++){
r
= GetRand(0,1,10000);
if(r>Pm){
t_h[n]
= r;
continue;
}
t_h[n]
= r;
int pos = (int)GetRand(0,20,20);
Fh_swap(P[n],pos);
}
}

最简单的单点变异,一定概率进行变异即可……

注意每一个基因都有一个自身的可能性进行变异,每一次并不一定相同。

问题:实验结果中的基因总是会集中到x1较大,x2较小的情况。但x1,x2同取极小时的情况并未在多次试验的结果中出现。不得其解……

posted on 2011-05-13 14:43  Ethan.Wong  阅读(633)  评论(0编辑  收藏  举报