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

   最近在學習有關遺傳算法和神經網絡方面的知識,網上查看了很多這方面的祕笈,只怪小生天生愚鈍、才疏學淺,不能很好的領悟祕笈中的真諦,往往被弄得暈頭轉向、不知所措快哭了委屈。直到有一天無意中看到了博主zzwu寫的有關這方面的文章,初讀之,如溫舊習;漸深入,覺甚好;遂一氣呵成,猶如撥雲見日、茅塞頓開。餘甚怕在茫茫Internet中再無機會拜讀之,遂收藏於此,以便衆人觀之,絕無其他不良用途。在此對博主再次深表感謝。

博文轉自:http://blog.csdn.net/zzwu/article/details/561625



.
(連載之六)
.
扎自<遊戲編程中的人工智能技術>第三章

 清華大學出版社

(本章由zzwu譯)

 

3.4.1爲染色體編碼
(Ecoding the Chromosome)

          
   每個染色體必須把小人Bob 的每一個行動編入代碼中。Bob的行動僅限爲4個方向向東(East),向南(South),向西(West),向北(North)
    
故編碼後的染色體應該就是代表這4個方向信息的一個字符串。傳統的編碼方法就是把方
向變換成二進制的代碼。四個方向只要2位就夠了,例如下表所示的那樣:
      
二進制代碼 十進制譯碼 代表的方向
00 0 向北
01 1 向南
10 2 向東
11 3 向西

 這樣,如果你得到了一個隨機的二進制字符串,你就能將它譯碼出Bob行動時所遵循的

一系列方向。例如染色體:

                    111110011011101110010101

代表的基因就是
            11,11,10,01,10,11,10,11,10,01,01,01
			  
當把二進制代碼譯成十進制時,就成爲
            3,3,2,1,2,3,2,3,2,1,1,1

再把這些放進一個表格中,就可以使你相信這是一樣的一些概念: 
        
二進制代碼 十進制譯碼 代表的方向
11 3 West
11 3 West
10 2 East
01 1 South
10 2 East
11 3 West
10 2 East
11 3 West
10 2 East
01 1 South
01 1 South
01 1 South

      到此,你要做的全部就是將Bob置於迷宮的起點,然後告訴他根據這張表所列的方向一步步地走。如果按某一個方向前進將使Bob碰到牆壁或障礙物,則只需忽略該方向並繼續按下一個方向去走就行了。這樣不斷下去,直到所有方向用光或Bob到達出口時爲止。
    
    如果你想象有幾百個這樣的隨機的染色體,你就能看到它們中的某些可能爲Bob譯碼出到達出口的一套方向(問題的一個解),但它們中的大多數將是失敗的。
    
    遺傳算法以隨機的2進制串(染色體)作爲初始羣體,測試它們每一個能讓Bob走到離開出口有多麼接近,然後讓其中最好的那些來孵化後代,期望它們的子孫中能有比Bob走得離出口更近一點。這樣繼續下去,直到找出一個解,或直到Bob絕望地在一個角落裏被粘住不動爲止(你將看到,這種情況是可能發生的)。
      
    因此,我們應定義一種結構,其中包含一個2進制位串(染色體),以及一個與該染色體相聯繫的適應性分數。我把這個結構稱爲SGenome結構,它的定義如下:  
  

struct SGenome    {      vector <int>  vecBits;      double   dFitness;      SGenome():dFitness(0){}      SGenome(const int num_bits):dFitness(0)      {        //創造隨機二進制位串        for (int i=0; i<num_bits; ++i)          {             vecBits.push_back(RandInt(0,1));          }      }   };     
正如你能見到的那樣,如果你在創建SGenome對象時把一個整型數作爲參數傳遞給構造函數,則它就會自動創建一個以此整數爲長度的隨機2進制位串,
並將其適應性分數初始化爲零,這樣就把基因組什麼都準備好了。
  
程序註釋
    std::vectorSTL(Standard Templete Library)標準模板庫的一部分, 這是一種爲處理動態數組而預先建立好的類。如果要把數據加入STL中,可使用
push_back()方法。

下面是一個簡單的例子:
  
  #include <vector>   for (int i=0; i<10; i++)     {         MyFirstVector.push_back(i);             cout << endl << MyFirstVector[i];     }
要清空一個向量,使用clear()方法
        MyFirstVector.clear();

你可利用size()方法來得到向量中元素的數目

        NyFirstVector.size()

就是這樣。
  
    不需要你去考慮內存管理問題-std::vector能夠爲你來做所有這些!當需要時,我會在整個程序中使用它。

    SGenome結構中不具備怎樣爲染色體(vecBits)進行譯碼的知識; 這是需要由遺傳算法類自己來完成的一項任務。現在讓我們來快速窺視一下這個類的定義。
我已把它稱作CgaBob類(有時我對我的原始創見自己也很喫驚,但我確實是這樣做的)。
class CgaBob
{
private:
//基因組羣體
vector<SGenome> m_vecGenomes
//羣體的大小
int m_iPopSize
double m_dCrossoverRate;
double m_dMutationRate;
//每個染色體含有多少bits
int m_iChromoLength;
//每個基因有多少bits
int m_iGeneLength;
int m_iFittestGenome;
double m_dBestFitnessScore;
double m_dTotalFitnessScore;
int m_iGeneration;
//爲 map 類創建一個實例
CBobsMap m_BobsMap;
//另一個CbobsMap對象用來保存每一代的最佳路徑的一個記
//錄,這是被訪問小格的一個數組,它僅僅是爲了顯示目的而使用的。
CBobsMap m_BobsBrain;
//讓你知道運行是否仍在進行中
bool m_bBusy;
void Mutate(vector<int>&vecBits);
void Crossover(const vector<int>&mum,  
               const vector<int>&dad,  
               vector<int>&baby1,
               vector<int>&baby2);
SGenome& RouletteWheel Selection();
//用新的適應性分數來更新基因組原有的適
//應性分數,並計算羣體的最高適應性分數和適應性分數最高的那個成員。
void UpdateFitnessScores();
//把一個位向量譯成爲一個方向的(整數)向量
vector<int> Decode(const vector<int> &bits);
//把一個位向量變換爲十進制數。用於譯碼
int BinToInt(const vector<int> &v);
//創建一個隨機的二進制位串的初始羣體
void CreateStartPopulation();
public:
CgaBob(double cross_rat,
        double mut_rat,
       int pop_size,
       int num_bits,
      int gene_len):m_dCrossoverRate(cross_rat),
                      m_dMutationRate(mut_rat),
                      m_iPopSize(pop_size),
                      m_iChromoLength(num_bits),
                      m_dTotalFitnessScore(0.0),
                      m_iGeneration(0),
                      m_iGeneLength(gene_len),
                      m_bBusy(false)
{
  CreateStartPopulation();
}
void Run(HNND hwnd);
void Epoch();
void Render(int cxClient, int cyClient, HDC surface);
//訪問用的方法
int Generation(){return m_iGeneration;}
int GetFittest(){return m_iFittestGenome;}
bool Started(){return m_bBusy;}
void Stop(){m_bBusy = false;}
};

由上你可看出,當這個類的一個實例被創建時,構造函數初始化所有的變量,並調用CreateStartPopulation()。這一短小函數創建了所需數量的基因組羣體。

每個基因組一開始包含的是一個由隨機2進制位串組成的染色體,其適應性分數則被設置爲零。


-連載6完-

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