3.4.1 算子函数
重新阐述一下遗传算法的各种操作(或称算子)函数——选择,杂交,变异——的代码。读者在了解遗传算法的知识时对它们具有更确切的认识。
3.4.1.1 重温赌轮选择
首先讲述赌轮选择算法,这一个函数的功能是从群体中选择一个基因组,选中的几率正比于基因组的适应性分数。
SGenome
&
CgaBob::RouletteWheelSelection()
...
{
double fSlice=RandFloat()*m_dTotalFitnessScore...{123456789};在从零到整个适应性分数范围内随机选取了一实数fSlice。可以将此数看作整个适应性分数饼图中的一块。
double fSlice=RandFloat()*m_dTotalFitnessScore...{123456789};在从零到整个适应性分数范围内随机选取了一实数fSlice。可以将此数看作整个适应性分数饼图中的一块。
double
cfTotal
=
0
;
int SelectedGenome = 0 ;
for ( int i = 0 ;i < m_iPopSize;i ++ ) ... {
cfTotal+=m_veceenomes[i].dFitness;
if(cfTotal>fSlice)...{
SelectedGenome=i;
break;
}
}
return m_vecGenomes[SelectedGenome];
}
int SelectedGenome = 0 ;
for ( int i = 0 ;i < m_iPopSize;i ++ ) ... {
cfTotal+=m_veceenomes[i].dFitness;
if(cfTotal>fSlice)...{
SelectedGenome=i;
break;
}
}
return m_vecGenomes[SelectedGenome];
}
现在,程序通过循环来考察各个基因组,把它们相应的适应性分数一个一个累加起来,直到这一部分累加的和大于fSlice的值时,即返回该基因组。
3.4.1.2 重温杂交算子
这一函数要求两个染色体在同一随机位置上断裂开来,然后将它们在断开点以后的部分进行互换,以形成两个新的染色体(子代)。
void
CgaBob::Crossover(
const
vector
<
int
>
&
mum,cont vector
<
int
>
&
dad,
vector < int > & baby1, vector < int > & baby1) ... {这个函数共传入4个参数,参数传递均采用引用(referenece)方式,其中前两个传入父代染色体(这里染色体只是一个整数型的集合std::vector),后2个则是用来复制子代染色体的空集合。
vector < int > & baby1, vector < int > & baby1) ... {这个函数共传入4个参数,参数传递均采用引用(referenece)方式,其中前两个传入父代染色体(这里染色体只是一个整数型的集合std::vector),后2个则是用来复制子代染色体的空集合。
if
((RandFloat()
>
m_dCrossoverRate)
||
(mum
==
dad))
...
{
baby1=mum;
baby2=dad;
return;
}
baby1=mum;
baby2=dad;
return;
}
上面一段程序的作用为:首先进行检测,决定mum和dad两个父辈是否需要进行杂交。杂交发生的概率是由参数m_dCrossoverRate确定的。如果不发生杂交,则两个父辈染色体不作任何改变地就直接复制给子代,函数返回。
int
cp
=
RandInt(
0
,m_iChromoLangth
-
1
);
for
(
int
i
=
0
;i
<
cp;i
++
)
...
{
baby1.push_back(mum[i]);
baby2.push_back(dad[i]);
}
for (i = cp;i < mum.size();i ++ ) ... {
baby1.push_back(dad[i]);
baby2.push_back(mum[i]);
}
}
baby1.push_back(mum[i]);
baby2.push_back(dad[i]);
}
for (i = cp;i < mum.size();i ++ ) ... {
baby1.push_back(dad[i]);
baby2.push_back(mum[i]);
}
}
这两个小循环把2个父染色体在杂交点(cp)以后的所有位进行了互换,并把新的染色体赋给了2个子代。
3.4.1.3 重温变异算子
这个函数所做的工作是沿着一个染色体的长度,一位一位地进行考察,并按m_dMutationRate给定的几率,将其中某些位进行翻转。
void
CgaBob::Mutate(vector
<
int
>
&
vecBits)
...
{
for(int curBit=0;curBit<vecBits.size();curBit++)...{
//是否要翻转此位
if(RandFloat()<m_dMutationRate)...{
//是翻转此位
vecBits[curBit]=!vecBits[curBit];
}
}//移到下一位
}
for(int curBit=0;curBit<vecBits.size();curBit++)...{
//是否要翻转此位
if(RandFloat()<m_dMutationRate)...{
//是翻转此位
vecBits[curBit]=!vecBits[curBit];
}
}//移到下一位
}
至此,完成第一个遗传算法程序。
3.4.2 运行寻路人程序
运行Pathfinder程序时,程序不是每次都能找到一条通往出口的路径。Bob有时会在一个局部地区不确定地走来走去,试着寻找他的回家路。这主要是由于群体太快地收敛到一个特殊类型的染色体。这样,由于群体中的成员变得如此相似,crossover算子的优势这时实际上已经不能发挥作用,所有发生的事情都是靠总量很少的变异操作在起作用。但因变异率设置很低,当染色体类型的差异消失后,仅仅依靠变异本身已不能去发现一个解。另外,由于赌轮选择的工作方式,使得任何一代的最合适的染色体无法保证传到下一代。
在后面的章节中,将会谈到这些问题,并介绍一些技术来帮助维护基因组的差异性且同时能保留那些较好的基因组。但在这里,首先介绍一些不同的编码方法,并考察它们如何和使用中可能遇到的问题类型关联在一起。
3.4.3 二进制数转换3个问题的答案
3
.5 练习
本章起每一章的后面,都会给出一些问题。不强调这对编程有多么重要,但是这是惟一能使读者对那些算法产生“感性”认识的方法。而且,当开始去做复杂的题目时,这种“感性”认识将变得非常重要。
1.为杂交率,突变率,群体尺寸,染色体长度等参数设置各种不同的值来进行试验,观察它们对算法的效率有什么影响?
2.尝试去掉杂交操作,而增加突变率,会出现什么后果?如果只用杂交操作,而不利用突变,又会发生什么?
3.修改适应性分数的计算函数,使多次进入同一小格的染色体到到惩罚。这应该导致更有效的到出口的路径。
4.用别的什么办法能够使路径变得更为有效?