遺傳算法是在隨機的初始數據下,經過一段時間的變化,最後收斂得到針對某類特定問題的一個或者多個解。
主要步驟有編碼 選擇 交叉 變異
這裏以一個極其簡單的探索迷宮出路的代碼爲例 增加對遺傳算法的感性認識。
編碼
2,3,4,1,1,1,1,1,1,1,1,1,1,1,1,
1,8,1,0,0,0,0,0,0,0,1,0,0,1,1,
1,0,1,0,1,0,1,1,1,0,0,0,0,0,1,
1,0,1,0,1,0,1,1,1,1,0,1,0,1,1,
1,0,0,0,1,0,0,0,0,0,0,1,0,5,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
我們定義這樣一個迷宮
1表示爲不可到達地點
0表示可通過區域
5是起點 8是終點
我們再定義一系列行走出迷宮的行走方向數組 walk[TestStep](walk[50]).這個數組記錄了走50步的方向
方向使用0-3來代替 0 表示向上走,1表示向下走,2表示向左走,3表示向右走。然後測試這一系列走法在迷宮能達到的座標,以達到的位置和終點的X\Y的差值的倒數作爲這個走法的評分
price = 1/(1.0+(abs(exitIndex.x-vMethods[i].currentPosIndex.x)+abs(exitIndex.y-vMethods[i].currentPosIndex.y)));
考慮到X\Y的差值可能爲零,所以額外加了一個1.0;以上就是編碼的步驟;
選擇
作爲進化演變下一代的元素,我們需要選擇評分比較高的走法
通常的辦法是輪盤賭選擇 根據評分的高低 決定其被選中的機率
各個個體被選中的概率與其適應度函數值大小成正比。設羣體大小爲n ,個體i 的適應度爲 Fi,則個體i 被選中遺傳到下一代羣體的概率爲:
比如說
走法A 評分0.5
走法B 評分0.2
走法C 評分0.3
那麼根據一個隨機範圍爲0-99的隨機數
如果數目在0-49之間 則選擇走法A
如果數目在50-69之間則選擇走法B
如果數目在70-99之間則選擇走法C
我的代碼中 寫的比較簡單 直接選擇評分在前一半的作爲進化演變的元素
int index1 = rand()%(TrySize/2);
int index2 = rand()%(TrySize/2);
缺點是可能逐步演化中 族羣的走法都慢慢接近 甚至編程一樣 從未導致沒有變化 得不到正確結果
優點是 代碼簡單
交叉
2個元素交換部分結構,來構造下一代新的元素
例如
走法A 01230123 0123
走法B 12301230 1230
構造下一代走法
01230123 1230
代碼中爲
Method m = CrossOver(vMethods[index1],vMethods[index2]);
突變
對元素數據進行小概率的調整 以調整進化的集中性 避免所有元素同一化
代碼中是以25%的概率 對某一步的走法進行調整
if((rand()%100)>75)
{
int index = rand()%TestStep;
m.walk[index] = direction(rand()%4);
}
本節代碼屬於實驗性代碼 對具體參數和一些算法都做了簡化 只是加深對算法的理解 方便入門
至於算法的選擇 參數優化調整的理論依據 均未涉及 需要學習理論教程書籍
代碼如下
1 #include <iostream> 2 #include <ctime> 3 #include <cstdlib> 4 #include <vector> 5 #include <algorithm> 6 7 using namespace std; 8 9 int mapArray[6][15] = { 2,3,4,1,1,1,1,1,1,1,1,1,1,1,1, 10 1,8,1,0,0,0,0,0,0,0,1,0,0,1,1, 11 1,0,1,0,1,0,1,1,1,0,0,0,0,0,1, 12 1,0,1,0,1,0,1,1,1,1,0,1,0,1,1, 13 1,0,0,0,1,0,0,0,0,0,0,1,0,5,1, 14 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; 15 struct Point{ 16 int x; 17 int y; 18 }; 19 20 Point startIndex = {4,13}; 21 Point exitIndex = {1,1}; 22 23 enum direction{ 24 Mup =0, 25 Mdown , 26 Mleft , 27 Mright }; 28 29 #define TestStep 50 30 #define TrySize 50 31 32 33 struct Method{ 34 direction walk[TestStep]; 35 Point currentPosIndex; 36 double price; 37 }; 38 39 vector<Method> vMethods; 40 vector<Method> vNewMethods; 41 42 43 bool SortByPrice(const Method& obj1,const Method& obj2) 44 { 45 return obj1.price>obj2.price; 46 } 47 48 void InitRandomWalk(direction walk[]){ 49 for(int i = 0;i< TestStep;++i){ 50 walk[i] = direction(rand()%4); 51 } 52 } 53 54 bool Walk(Point& currentPosIndex,direction walk[],int length){ 55 for(int i = 0;i< TestStep;++i){ 56 if(walk[i] == Mup){ 57 if( (currentPosIndex.x-1)>=0 && 58 mapArray[currentPosIndex.x-1][currentPosIndex.y] != 1){ 59 currentPosIndex.x -= 1; 60 } 61 }else if(walk[i] == Mdown){ 62 if( (currentPosIndex.x+1)<=5 && 63 mapArray[currentPosIndex.x+1][currentPosIndex.y] != 1){ 64 currentPosIndex.x += 1; 65 } 66 }else if(walk[i] == Mleft){ 67 if( (currentPosIndex.y-1)>=0 && 68 mapArray[currentPosIndex.x][currentPosIndex.y-1] != 1){ 69 currentPosIndex.y -= 1; 70 } 71 }else if(walk[i] == Mright){ 72 if( (currentPosIndex.y+1)<=14 && 73 mapArray[currentPosIndex.x][currentPosIndex.y+1] != 1){ 74 currentPosIndex.y += 1; 75 } 76 } 77 if(currentPosIndex.x == exitIndex.x && currentPosIndex.y == exitIndex.y){ 78 return true; 79 } 80 } 81 82 return false; 83 } 84 85 Method CrossOver(const Method& m1,const Method& m2){ 86 int i = rand()%TestStep; 87 Method m; 88 89 for(int j = 0; j <= i ;++j){ 90 m.walk[j] = m1.walk[j]; 91 } 92 93 for(int k =i;k < TestStep;++k){ 94 m.walk[k] = m2.walk[k]; 95 } 96 return m; 97 } 98 99 bool run(){ 100 101 for(int i = 0;i < TrySize;++i){ 102 vMethods[i].currentPosIndex.x = startIndex.x; 103 vMethods[i].currentPosIndex.y = startIndex.y; 104 if(Walk(vMethods[i].currentPosIndex, vMethods[i].walk,TestStep)){ 105 cout << "walk to exit!!!" << endl; 106 return true; 107 } 108 vMethods[i].price = 1/(1.0+(abs(exitIndex.x-vMethods[i].currentPosIndex.x)+abs(exitIndex.y-vMethods[i].currentPosIndex.y))); 109 cout << "current pos:\t" << vMethods[i].currentPosIndex.x <<" " << vMethods[i].currentPosIndex.y <<"\tprice:"<< vMethods[i].price<< endl; 110 } 111 112 sort(vMethods.begin(),vMethods.end(),SortByPrice); 113 for(int i = 0 ; i <TrySize;i++){ 114 int index1 = rand()%(TrySize/2); 115 int index2 = rand()%(TrySize/2); 116 117 // for(int k = 0 ;k<TestStep;++k){ 118 // cout << vMethods[index1].walk[k]; 119 // } 120 // cout << endl; 121 122 Method m = CrossOver(vMethods[index1],vMethods[index2]); 123 if((rand()%100)>75) 124 { 125 int index = rand()%TestStep; 126 m.walk[index] = direction(rand()%4); 127 128 } 129 vNewMethods.push_back(m); 130 // for(int k = 0 ;k<TestStep;++k){ 131 // cout << vMethods[index1].walk[k]; 132 // } 133 // cout << endl; 134 } 135 vMethods = vNewMethods; 136 vNewMethods.clear(); 137 return false; 138 } 139 140 141 142 143 144 int main(int argc, char *argv[]) 145 { 146 vMethods.clear(); 147 srand( (unsigned)time( NULL ) ); 148 for(int i = 0;i < TrySize;++i){ 149 Method m; 150 InitRandomWalk( m.walk); 151 vMethods.push_back(m); 152 } 153 154 int64_t count = 0; 155 while(!run()){ 156 count++; 157 } 158 cout << "run " << count << " times." << endl; 159 160 161 return 0; 162 }