前两天写了一个遗传算法求最优解的程序,今天拿出来简单整理一下。
程序要解决的问题:
最优解问题:四个变量取何值,该函数取最大值时?(取值范围-5到5) 显然,当四个未知数都为零的时候,函数取最大值,但如何利用遗传算法来实现,才是要研究的问题。
上代码:
1.染色体结构定义define.h
struct chromosome
{
vector<double> s_chromosome;
double s_value;
double s_fit;
};
#define gene double
#define pool vector<struct chromosome>
2.遗传算法类定义,以及辅助函数GA.h
class GA
{
public:
GA(
int N,
double pc,
double pm);
GA(
int N);
~GA(
void);
bool initial_pool();
int run(
int Maxnum,
double d);
double get_best_value();
chromosome get_best_chro();
private:
int c_n;
double c_Pc;
double c_Pm;
gene c_gene_tmp;
chromosome c_chromosome_temp;
pool c_pool;
bool eval_pool();
bool selection();
bool crossover();
bool mutation();
};
inline double get_5_5_rand()
{
random_device rd;
double a=
double((rd()*
6553)%
100000)/
100000;
a=(a-
.5)*
10;
return a;
}
inline int get_0_3_rand()
{
random_device rd;
int a= rd()%
4;
return a;
}
inline double get_0_1_rand()
{
random_device rd;
double a=
double((rd()*
6553)%
100000)/
100000;
return a;
}
inline double get_chro_value(chromosome chro)
{
double a=
0,tmp=
0;
for(
int i=
0;i<chro.s_chromosome.size();i++)
{
tmp=tmp+chro.s_chromosome[i]*chro.s_chromosome[i];
}
a=
1/(tmp+
1);
return a;
}
3.遗传算法成员函数实现GA.cpp
#include "GA.h"
inline bool sort_value(
const chromosome &aim1,
const chromosome &aim2);
GA::GA(
int N,
double pc,
double pm)
{
c_n=N;
c_Pc=pc;
c_Pm=pm;
}
GA::GA(
int N)
{
c_n=N;
c_Pc=
0.5;
c_Pm=
0.1;
}
GA::~GA(
void)
{
cout<<
"清理GA类内存------------------"<<endl;
}
bool GA::initial_pool()
{
c_pool.clear();
for(
int j=
0; j < c_n;j++)
{
c_chromosome_temp.s_chromosome.clear();
for(
int i =
0;i<
4;i++)
{
c_chromosome_temp.s_chromosome.push_back(
get_5_5_rand());
}
c_chromosome_temp.s_value=
0;
c_pool.push_back(c_chromosome_temp);
}
cout<<
"初始值:"<<endl;
cout<<
"x1:"<<get_best_chro().s_chromosome[
0]
<<
" x2:"<<get_best_chro().s_chromosome[
1]
<<
" x3:"<<get_best_chro().s_chromosome[
2]
<<
" x4:"<<get_best_chro().s_chromosome[
3]<<endl;
return true;
}
bool GA::eval_pool()
{
int i=
0;
while (i!=c_pool.size())
{
double temp=
0;
for(
int j=
0;j<c_pool[i].s_chromosome.size();j++)
{
temp = temp+
c_pool[i].s_chromosome[j]*c_pool[i].s_chromosome[j];
}
c_pool[i].s_value=
1/(temp+
1);
i++;
}
sort(c_pool.begin(),c_pool.end(),sort_value);
return true;
}
inline bool sort_value(
const chromosome &aim1,
const chromosome &aim2)
{
return (aim1.s_value>aim2.s_value);
}
bool GA::selection()
{
double all_value=
0;
int i=
0;
while(
true)
{
all_value+=c_pool[i].s_value;
i++;
if(i>=c_pool.size())
{
i=
0;
break;
}
}
while(
true)
{
c_pool[i].s_fit=c_pool[i].s_value/all_value;
i++;
if(i>=c_pool.size())
{
i=
0;
break;
}
}
pool tmp_pool;
for(
int k=
0;k<c_pool.size();k++)
{
double m=
0,r=get_0_1_rand();
for(
int j=
0;j<c_pool.size();j++)
{
m=m+c_pool[j].s_fit;
if(r<=m)
{
tmp_pool.push_back(c_pool[j]);
break;
}
}
}
c_pool.clear();
c_pool=tmp_pool;
return true;
}
bool GA::crossover()
{
vector<chromosome>::iterator it=c_pool.begin();
pool cross_pool,no_cross_pool;
for(
int i=
0;i<c_pool.size();i++)
{
double a=get_0_1_rand();
if (a<c_Pc)
cross_pool.push_back(c_pool[i]);
else
no_cross_pool.push_back(c_pool[i]);
}
if(cross_pool.size()==
0)
{
c_pool.clear();
c_pool=no_cross_pool;
return true;
}
else if (cross_pool.size()==
1)
{
c_pool.clear();
c_pool=no_cross_pool;
c_pool.push_back(cross_pool[
0]);
return true;
}
else
{
chromosome cross_tmp1,cross_tmp2,cross_1,cross_2;
for(
int i=
0;i<
4;i++)
{
cross_tmp1.s_chromosome.push_back(
0);
cross_tmp2.s_chromosome.push_back(
0);
}
if (cross_pool.size()%
2==
0)
{
for(
int i=
0;i<cross_pool.size();i+=
2)
{
int lc=get_0_3_rand();
for(
int j=
0;j<
4;j++)
{
cross_1=cross_pool[i];
cross_2=cross_pool[i+
1];
if(lc<j)
{
cross_tmp1.s_chromosome[j]=cross_pool[i].s_chromosome[j];
cross_tmp2.s_chromosome[j]=cross_pool[i+
1].s_chromosome[j];
}
else
{
cross_tmp2.s_chromosome[j]=cross_pool[i].s_chromosome[j];
cross_tmp1.s_chromosome[j]=cross_pool[i+
1].s_chromosome[j];
}
}
if(get_chro_value(cross_tmp1)>get_chro_value(cross_1))
no_cross_pool.push_back(cross_tmp1);
else
no_cross_pool.push_back(cross_1);
if(get_chro_value(cross_tmp2)>get_chro_value(cross_2))
no_cross_pool.push_back(cross_tmp2);
else
no_cross_pool.push_back(cross_2);
}
}
else
{
no_cross_pool.push_back(cross_pool[
0]);
for(
int i=
1;i<cross_pool.size();i+=
2)
{
int lc=get_0_3_rand();
for(
int j=
0;j<
4;j++)
{
cross_1=cross_pool[i];
cross_2=cross_pool[i+
1];
if(lc<j)
{
cross_tmp1.s_chromosome[j]=cross_pool[i].s_chromosome[j];
cross_tmp2.s_chromosome[j]=cross_pool[i+
1].s_chromosome[j];
}
else
{
cross_tmp2.s_chromosome[j]=cross_pool[i].s_chromosome[j];
cross_tmp1.s_chromosome[j]=cross_pool[i+
1].s_chromosome[j];
}
}
if(get_chro_value(cross_tmp1)>get_chro_value(cross_1))
no_cross_pool.push_back(cross_tmp1);
else
no_cross_pool.push_back(cross_1);
if(get_chro_value(cross_tmp2)>get_chro_value(cross_2))
no_cross_pool.push_back(cross_tmp2);
else
no_cross_pool.push_back(cross_2);
}
}
c_pool.clear();
c_pool=no_cross_pool;
return true;
}
}
bool GA::mutation()
{
int i=
0;
while(
true)
{
double rm=get_0_1_rand();
chromosome bianyi=c_pool[i];
if(rm<c_Pm)
{
int lc=get_0_3_rand();
bianyi.s_chromosome[lc]=get_5_5_rand();
if(get_chro_value(bianyi)>get_chro_value(c_pool[i]))
c_pool[i]=bianyi;
}
i++;
if(i>=c_pool.size())
{
i=
0;
break;
}
}
return true;
}
int GA::run(
int Maxnum,
double d)
{
int i=
0;
double last_value=
0,current_value;
while (
true)
{
i++;
eval_pool();
current_value=c_pool[
0].s_value;
selection();
crossover();
mutation();
if(i%
40==
0)
cout<<
"current_value:"<<current_value<<endl;
if(
abs(current_value-last_value)<d)
{
}
if(i>=Maxnum)
{
cout<<
"超出迭代次数,算法结束!"<<endl;
break;
}
last_value=current_value;
}
eval_pool();
return i;
}
double GA::get_best_value()
{
return c_pool[
0].s_value;
}
chromosome GA::get_best_chro()
{
return c_pool[
0];
}
4.主函数main.cpp
#include "GA.h"
#include "test.h"
#define ERROR 0.00000001
#define MAXNUM 1000
int main()
{
GA my_GA(
10)
my_GA
.initial_pool()
cout<<
"迭代次数:"<<my_GA
.run(MAXNUM,ERROR)<<endl
cout<<
"最优解:"<<my_GA
.get_best_value()<<endl
cout<<
"x1:"<<my_GA
.get_best_chro()
.s_chromosome[
0]
<<
" x2:"<<my_GA
.get_best_chro()
.s_chromosome[
1]
<<
" x3:"<<my_GA
.get_best_chro()
.s_chromosome[
2]
<<
" x4:"<<my_GA
.get_best_chro()
.s_chromosome[
3]<<endl
system(
"pause")
}
小结:
在程序编写过程中,遇到了几个小问题
第一个是随机数生成,由于需要每次调用随机生成一个随机数,采用 srand((unsigned)time(NULL)); 的方法发现每一次生成的随机数都是一样的规律,不能用于遗传算法,网上查了一下发现,c++自带了一种随机数生成器,可以产生较为“随机”的随机数,最起码每次的规律是不相同的。使用方法:
#include <random>
inline int get_0_3_rand()
{
random_device rd;
int a= rd()%
4;
return a;
}
第二个问题是种群交叉变异的过程中,如果没有添加“如果子代没有父代好,就放弃这次交叉或者变异”,导致程序收敛效果很不好,故而加了这一限制条件,收敛情况大有改善。
if(get_chro_value(cross_tmp1)>get_chro_value(cross_1))
no_cross_pool
.push_back(cross_tmp1);
else
no_cross_pool
.push_back(cross_1);
if(get_chro_value(cross_tmp2)>get_chro_value(cross_2))
no_cross_pool
.push_back(cross_tmp2);
else
no_cross_pool
.push_back(cross_2);
第三个问题是种群规模的选择,在选择种群规模时要同时考虑算法计算速度和整体收敛速度的关系,最后设置了种群规模为10,迭代次数1000时已经能够达到很好的收敛效果,实际上如果加大种群规模,迭代次数也将大为减少,为了便于观察,选择了这一数字,发现程序收敛效果比较不错。
参考:
《计算智能》 张军 詹志辉等 清华大学出版社