遗传算法 练习

来源:互联网 发布:怎么查看mac运行程序 编辑:程序博客网 时间:2024/06/10 03:13

一、 实验目的

1. 熟悉和掌握遗传算法的原理、实质。

2. 学会使用遗传算法解决问题。

3. 学会编写遗传算法程序寻找函数最值。

 

 

二、 实验内容

 

1. 用遗传算法解决下面函数的极大值问题。   

 

参数设定可参考如下:

种群大小M=10

交叉概率 pc=0.6

变异概率 pm=0.02

迭代次数最多为100

 

三、 算法实现

 

#include<bits/stdc++.h>

#define de(x) cout<<#x<<"="<<x<<endl

#define rep(i,a,b) for(int i=a;i<b;i++)

using namespace std;

const int M=10;

const double pc=0.6;

const double pm=0.02;

const int MAXT=100;

const int gn=5;

struct unit

{

int bit[gn];  

int suit;

} gen[MAXT+5][M];

int gennum=0;

int maxx;

int maxgen;

unit tmp;

unit encode(int x)

{

int i=gn-1;

while(x)

{

tmp.bit[i--]=x%2;

x>>=1;

}

return tmp;

}

int decode(unit tmp)

{

int ans=0;

int i=0;

while(i!=gn) ans=ans*2+tmp.bit[i++];

return ans;

}

int fixable(unit u)

{

int s=decode(u);

return s*s;

}

void choose(int oldgen)//给出一个老一代的群体 选择出M个返回新一代群体的id

{

double p[M];

double sumf=0;

double sump[M];

int tmp=0;

memset(p,0,sizeof(p));

memset(sump,0,sizeof(sump));

rep(i,0,M) {

tmp=decode(gen[oldgen][i]);

sumf+=tmp;

if(tmp>maxx){maxx=tmp;maxgen=oldgen;}

}

de(sumf);

for(int i=0;i<M;i++) {p[i]=decode(gen[oldgen][i])*1./sumf;}

sump[0]=p[0];

for(int i=1;i<M;i++) sump[i]=sump[i-1]+p[i];

// for(int i=0;i<M;i++) cout<<sump[i]<<" ";cout<<endl;

rep(k,0,M){

double ran=rand()/double(RAND_MAX);

if(ran<=sump[0])

rep(j,0,gn) gen[oldgen+1][k].bit[j]=gen[oldgen][0].bit[j];

else

rep(i,0,M) {//M个个体 如果这个个体满足条件 插入并

if(sump[i]>=ran)

{/* cout<<"ran="<<ran<" ";

cout<<"选择了"<<decode(gen[oldgen][i])<<endl;*/

rep(j,0,gn) gen[oldgen+1][k].bit[j]=gen[oldgen][i].bit[j];

break;

}

}

}

gennum++;

}

 

void cross(int nowgen)

{

for(int k=0;k*2<M*pc;k++)

{

int a=rand()%M;

int b=rand()%M;

int pos=rand()%gn;

for(int i=0;i<=pos;i++) swap(gen[nowgen][a].bit[i],gen[nowgen][b].bit[i]);

}

}

 

int vary(int nowgen)

{

int k=M*gn*pm;

for(int i=0;i*2<k;i++)

{

int n=rand()%M;//变异个体

int m=rand()%gn;//变异基因编号

gen[nowgen][n].bit[m]=1-gen[nowgen][n].bit[m];

}

}

 

void show(int i)

{

cout<<"第"<<i<<"代:"<<endl;

for(int j=0;j<M;j++) cout<<decode(gen[i][j])<<" ";

cout<<endl;

}

void init()

{

srand((unsigned)time(0));

gennum=0;

for(int i=0;i<M;i++)

{

for(int j=0;j<gn;j++)

gen[0][i].bit[j]=(rand()/double(RAND_MAX))>0.5?1:0;

}

show(0);

}

int main ()

{

init();

maxx=0;

for(int i=0;i<MAXT;i++)

{

choose(i);

cross(i+1); //交叉

vary(i+1);// 变异

show(i+1); //显示

}

cout<<"近似最优解是来自第"<<maxgen<<"代的"<<maxx<<endl;

return 0;

}

 

四、 实验结果

当参数为:

时;

 

 

 

 

五、 实验体会

本次实验主要学习了遗传算法的简单应用。遗传算法主要是模拟

->群体->环境选择->产生下一代群体->环境选择...的过程。

算法设计方面:

1. 要设计编码函数,解码函数用来将直观个体和字符编码(基因)相互转义。

2. 设计合理的选择函数,本次实现中用了常用的赌轮法,将个体的适应度对应为选择该个体的几率。

3. 设计交叉变异函数,用来将字符编码进行相互交配,产生下一代群体。

4. 对于每一代,在交叉产生下一代时是否同时把父母替代,是否保留最优的N个,是否淘汰最差等等有不同的实现方法。

 

 

代码实现方面:

1. 设计合理的数据结构存储基因串,可以降低时间复杂度。

2. 利用预定义和宏扩大程序的适用范围。

 

本次实验的收获:

1. 学到了基于估计计算确定需要交叉(变异)的个体(基因)数量的方法。

2. 大概学习了C++中srand() rand()的原理。

Rand()是一个伪随机函数,srand()用来设置rand()函数中的seed种子,如果seed是一个定值的话,得到的序列是确定的,如果seed是一个time(0)等其他动态变化的参数的话就可以模拟随机函数】

3. 第一次用代码实现赌轮法。

4. BUGS

a) 又一次被sum[i]+=p[i]这个我的经典错误代码坑到了

b) 脑子一抽写出了i!=i这样的取反函数...

c) 边界条件错了一发,调了挺久才发现。比如赌轮法中如果有若干个0取值需要注意不能取到0的上一个或下一个(如果是连续的0则还会取到0),应该用> <跳过0的情况。