您的位置:首页 > 理论基础 > 计算机网络

遗传算法入门(连载之七)

2016-04-02 11:27 435 查看
    最近在学习有关遗传算法和神经网络方面的知识,网上查看了很多这方面的秘笈,只怪小生天生愚钝、才疏学浅,不能很好的领悟秘笈中的真谛,往往被弄得晕头转向、不知所措



。直到有一天无意中看到了博主zzwu写的有关这方面的文章,初读之,如温旧习;渐深入,觉甚好;遂一气呵成,犹如拨云见日、茅塞顿开。余甚怕在茫茫Internet中再无机会拜读之,遂收藏于此,以便众人观之,绝无其他不良用途。在此对博主再次深表感谢。

博文转自:http://blog.csdn.net/zzwu/article/details/561626





(连载之七)
.
扎自<游戏编程中的人工智能技术>第三章

.

 清华大学出版社

.

3.4.2 Epoch (时代)

 遗传算法类中最烩灵人口的内容就是 Epoch()方法。这就是我们前面3.3节讲过的遗传算法的那个循环。它是这个类的工作部门(workhorse)。这一方法与所有工作或多或少都有连系。下面就让我们来更近距离地考察它 ...

void CgaBob::epoch()
{
UpdateFitnessScores();

在每一个 epoch 循环内所要做的第一件事情,就是测试染色体群中每一个成员的适应性分数。 UpdateFitnessScores() 是用来对每个基因组的二进制染色体编码进行译码的函数,而由它再把译码所得到的一系列结果,也就是由代表东、南、西、北四个方向的整数,发送给 CBobsMap::TestRoute 。后者检查Bob在地图中游走了多远,并根据Bob离开出口的最终距离,返回一个相应的适应性分数。让我通过很少几行源码来告诉你怎样计算Bob的适应性分数:

int DiffX = abs(posX - m_iEndX);
int DiffY = abs(posY - m_iEndY);

这里,DiffX和DiffY 就是Bob所在格子相对于迷宫出口的水平和垂直偏离值。试考察图 3.6 的例子。灰色小格代表Bob通过迷宫的路程,上面写着B的小格是他最终所到达的地方。在这一位置上,Diffx = 3,而 DiffY = 0。


return 1/(double)(DiffX+DiffY+1);
 
上面一行程序就是计算Bob的适应性分数,它把DiffX,DiffY这两个数字加起来,然后求倒数。DiffX,DiffY的和中还加了一个1,这是为了避免分母出现0的错误。如果Bob到达出口,DiffX+DiffY=0.
UpdateFitnessScores 也保持对每一代适应性分数最高的基因组以及所有与基因组相关的适应性分数的跟踪。这些数值在执行赌轮选择要使用。




图 3.6 Bob尝试寻找迷宫出口


      这最后一行式子就是计算 Bob 的适应性分数。它把 DiffX与DiffY 两个数字加起来然后求倒数。DiffX与DiffY的和数中还加了一个1,这是为了确保除法不会出现一个分母为零的错误,如果 Bob到达出口,Diffx + DiffY = 0。

     UpdateFitnessScores 也保持对每一代里适应分最高的基因组、以及与所有基因组相关的适应性分数的跟踪。这些数值在执行轮盘赌选择时需要使用。到此,你已经知道了函数 UpdateFitnessScores() 所做的全部工作,让我们回到 Epoch 函数的讨论 ...

     由于在每一个Epoch中都需要创建一个新的基因组群,因此,当它们在创建出来时(每次2个基因组),我们需要寻找一些地方来保存它们。

   //现在创建一个新的群体

   int NewBabies = 0;   

  //为婴儿基因组创建存储器  

   vector<SGenome> vecBabyGenomes;

现在继续讨论遗传算法循环中所处理的各种事务。

   while (NewBabies < m_iPopSize)    

   {

    //用轮盘赌法选择 2 个上辈(parents)

     SGenome mum = RouletteWheelSelection();   

     SGenome dad = RouletteWheelSelection();

    在每次迭代过程中,我们需要选择 2 个基因组来作为 2 个新生婴儿的染色体的上辈。我今后常喜欢把这2个上辈分别称为 dad (父亲)和 mum (母亲)因为他们将来就是要生孩子的)。你应该回忆得起来,一个基因组的适应性愈强,则由轮盘赌方法选择作为父母的几率也愈大。

   //杂交操作    

     SGenome baby1, baby2; 

     Crossover(mum.vecBits ,dad.vecBits, baby1.vecBits, baby2.vecBits);

    以上2行的工作是:创建 2 个空白基因组,这就是2个婴儿;它们与所选的上辈一起传递给杂交函数Crossover() 。这一函数执行了杂交(需要依赖于所设杂交率m_dCrossoverRate来进行),并把新的染色体的2进制位串存放到2个新生婴儿
baby1和baby2之中。


   // 变异操作  

    Mutate(baby1.vecBits);  

    Mutate(baby2.vecBits);

    以上这 2 步是对婴儿实行突变!这听起来可怕,但这对他们是有利的。一个婴儿的位的突变概率依赖于所选的参数 m_dMutationRate(突变率)。

   // 把2个新生因个婴儿加入新群体 

    vecBabyGenomes.push_back(baby1); 

    vecBabyGenomes.push_back(baby2); 

    NewBabies += 2; 

  }

    这 2 个新生后代最终要加入到新的群体中,这样就完成了一次 Loop 的迭代过程。这一过程需要不断重复,直到创建出来的后代总量和初始群体的大小相同。

   // 把所有婴儿复制到初始群体 

    m_vecGenomes = vecBabyGenomes; 

   // 代的计数加1 

   ++m_iGeneration; 

 }


    这里,原有的那个群体由新生一代所组成的群体来代替,并把代的计数器加1,以跟踪当前的代。就是这么一些了!呵呵,不难吧?

    这一 Epoch函数将无止境地重复,直到染色体收敛到了一个解,或到用户要求停止时为止。下面我将会向你显示上述各种操作(算子)的代码,但在此首先让我们来聊聊,应该如何确定使用的参数值。

-连载7完- 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息