一、实验内容
遗传算法(Genetic Algorithms,GA)是一种基于自然选择和自然遗传机制的搜索算法,它是一种有效的解决最优化问题的方法,属于一种进化算法。本实验要求采用简单遗传算法求解如下一元函数的最大值:
二、实验目的
- 熟悉人工智能系统中的问题求解过程;
- 熟悉遗传算法的求解过程与应用;
- 熟悉对遗传算法的建模、求解及编程语言的应用。
三、实验过程
1.算法基本原理与流程图;
算法的基本过程:
初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个体作为初始群体P(0)。
个体评价:计算群体P(t)中各个个体的适应度。
选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是建立在群体中个体的适应度评估基础上的。
交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。
变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。群体P(t)经过选择、交叉、变异运算之后得到下一代群体P(t+1)
终止条件判断:若t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。
算法有5个基本组成部分:
(1) 问题解的遗传表示;
(2) 创建解的初始种群的方法;
(3) 根据个体适应值对其进行优劣判定的评价函数;
(4) 用来产生新个体的遗传算子;
(5) 遗传算法的参数值。
算法一般结构可描述如下:
Begin
t=0
初始化 p(t)
评价 p(t)
While (终止条件不满足)do
begin
重组 p(t)(包括对 p(t)实施交叉和变异操作)
评价 p(t)
从 p(t)中选择 P(t+1)
t=t +1
end
endwhile
End
(1)编码
变量x作为实数,可以视为遗传算法的表现形式。从表现型到基因型的映射称为编码。二进制编码将某个变量值代表的个体表示为一个[0,1]二进制串(遗传算法最简单、最经典的编码方法)。若设定求解精确到6位小数,由于区间长度为2-(-1)=3,必须将区间[-1,2]分为3106等份。因为2097152=221<3106<=222=4194304,所以编码的二进制串长至少需要22位。
(2)产生初始种群
个体由串长为22的随机产生的二进制串组成染色体的基因码,产生一定数目的个体组成种群,种群的大小(规模)就是指种群中的个体数目。
(3)计算适应度
适应度大的个体其存活和被选中的概率大。适应度的计算就是对个体的计算,本例求函数最大值,目标函数值大的个体适应度大,所以直接引用目标函数作为适应度函数f(s)=f(x)。例如x1=0.637197通过编码得到的二进制串是s1=[1000101110110101000111],这个串就是个体。此个体的适应度就是:
假设选择种群数量为3 ,每个个体为s1=[1000101110110101000111], s2=[0000001110000000010000],s3=[1110000000111111000101],分别对应于数量值x1=0.637197, x2=-0.958973, x3=1.627888,另两个个体的适应度计算如下:
(4)选择
采用轮盘赌选择法,f(s1)=2.286345,f(s2)=1.078878,f(s3)=3.250650,把三个适应度依比例画在轮盘上,转动轮盘,最后停在哪里,就选择谁,适应度大的被选中概率大。
选择方法的实现:产生[0,1]间的随机数,以随机数落在那个范围进行选择:
[0,0.16),[0.16,0.51),[0.51,1]
(5)遗传操作
下面是经过选择操作后的两个个体:
s2=[00000 01110000000010000] ,
s3=[11100 00000111111000101],
首先执行单点交叉,随机选择交叉点,将之后的串交叉,得到新的两个个体为:
s2’=[00000 00000111111000101]
s3’=[11100 01110000000010000]
这两个个体的适应度分别为:
(6)变异操作
假定以一小概率选择S3的第5个遗传因子(第5位)变异,遗传因子由原来的0变为1,产生新个体:s3’=[1110100000111111000101],该个体适应度为:
个体的适应度比它的父个体的适应度减小了。
但如果选中第11个遗传因子变异,产生新个体为s3’’= [11100000001111111000101],适应度 ,又发现s3’'的适应度比其父个体的适应度改善了,这说明变异操作的“扰动”作用(全局搜索)。
- 基本数据结构分析和实现;
typedef struct node
{
char x[LEN];
double fitness,fitsum;
}node;
- 编写程序的各个子模块,按模块编写文档,含每个模块的建立时间、功能、输入输出参数意义和与其它模块联系等;
(1)编码:
double randd()
{
return (double)rand()/RAND_MAX;
}
int randi(int k)
{
return (int)(randd()*k+0.5);
}
(2)计算当前种群中各个个体的适应度函数
void cal_fitness()
{
int i,j,k;
double d;
for(i=0;i<SIZE;i++)
{
k=0;
for(j=LEN-1;j>=0;j--) k=(k<<1)+cur[i].x[j];
d=(double)k/N*3-1;
cur[i].fitness=d*sin(10*PI*d)+2;
cur[i].fitsum=i>0?(cur[i].fitness+cur[i-1].fitsum):(cur[0].fitness);
}
}
(3)选择函数,采取轮盘赌方式进行
int sel()
{
double p=randd();
double sum=cur[SIZE-1].fitsum;
for(int i=0;i<SIZE;i++)
{
if(cur[i].fitsum/sum>p) return i;
}
}
(4)换代
void tran()
{
int i,j,pos;
//找当前种群最优个体
max=cur[0];
for(i=1;i<SIZE-1;i++)
{
if(cur[i].fitness>max.fitness) max=cur[i];
}
for(int k=0;k<SIZE;k+=2)
{
//选择交叉个体
i=sel();
j=sel();
//选择交叉位置
pos=randi(LEN-1);
(5)交叉
if(randd()<P_CORSS)
{
memcpy(next[k].x,cur[i].x,pos);
memcpy(next[k].x+pos,cur[j].x+pos,LEN-pos);
memcpy(next[k+1].x,cur[j].x,pos);
memcpy(next[k+1].x+pos,cur[i].x+pos,LEN-pos);
}
else
{
memcpy(next[k].x,cur[i].x,LEN);
memcpy(next[k+1].x,cur[j].x,LEN);
}
//变异
if(randd()<P_MUTATION)
{
pos=randi(LEN-1);
next[k].x[pos]^=next[k].x[pos];
pos=randi(LEN-1);
next[k+1].x[pos]^=next[k+1].x[pos];
}
}
(6)找下一代的最差个体
min=next[0],j=0;
for(i=1;i<SIZE-1;i++)
{
if(next[i].fitness<min.fitness) min=next[i],j=i;
}//用上一代的最优个体替换下一代的最差个体
next[j]=max;
memcpy(cur,next,sizeof(cur));
cal_fitness();
}
四、实验结果,程序运行结果
详细步骤,打印种群:
五、结论
通过这次实验,我进一步学习了遗传算法,尽管编程结果没达到预期的结果,但是通过查阅相关资料,加深了我对人工智能的了解以及遗传算法的应用。明白了遗传算法以一种群体中的所有个体为对象,并利用随机化技术指导对一个被编码的参数空间进行高效搜索。其中,选择、交叉和变异构成了遗传算法的遗传操作;参数编码、初始群体的设定、适应度函数的设计、遗传操作设计、控制参数设定五个要素组成了遗传算法的核心内容。
六、实验心得
遗传算法模拟生物进化的基本过程,用数码串来类比生物中的染色个体,通过选择、交叉、变异等遗传算子来仿真生物的基本进化过程,利用适应度函数来表示染色体所蕴涵的问题解质量的优劣,通过适应度函数引导种群的进化方向,使种群不断“更新换代”,从而提高每代种群的平均适应度,并在此基础上,使得最优个体所代表的问题解逼近问题的全局最优解。在函数优化、机器学习、组合优化、人工神经元网络训练、自动程序设计、专家系统等领域都取得了广泛的应用。
“早熟”现象是遗传算法设计中的关键问题。遗传算法早熟收敛的本质特征是指群体中的各个个体非常相似, 群体的多样性急剧减少, 当前群体缺乏有效等位基因(最优解位串上的等位基因),在遗传算子 作用下不能生成高阶竞争模式。导致早熟现象的原因有:群体规模;群体初始化;选择压力;变异概率;适应度函数自身的性质。
预防“早熟”收敛的几种方式有:(1)选择合适的群体规模。(2)保持合适的选择压力。(3)适应度定标法。(4)参数适应性。(5)多种群进化。(6)Boltzmann选择法。
七、源程序
/*
用遗传算法求y=x*sin(10*pi*x)+2的最大值 -1=<x<=2
精确到6位小数
pow(2,21)<3*1000000<pow(2,22)
编码的二进制长度为22
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include