遺傳算法入門(連載之七)

    最近在學習有關遺傳算法和神經網絡方面的知識,網上查看了很多這方面的祕笈,只怪小生天生愚鈍、才疏學淺,不能很好的領悟祕笈中的真諦,往往被弄得暈頭轉向、不知所措快哭了委屈。直到有一天無意中看到了博主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完- 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章