学习了些人工智能的东西, 利用遗传算法来写了一个寻路的演示程序, 虽然该算法效率底下, 但作为一个学习程序还是有直观性;
先把代码和结果贴上, 有时间再写分析
遗传算法类
主要有两个类: 基因编码类和基因组类;
“GeGroup.h”
#ifndef _GEGROUP_HBB_
#define _GEGROUP_HBB_
#include "HMap.h"
#include "Global.h"
#include <bitset>
class HMap;
class GeCode
{
public:
GeCode();
~GeCode();
std::bitset<LEN_BIT_ROUTE> mGeRoute;
int mHighestFitSce;
int mHighestSceStep;
void Clear();
// decode generic
SearchDir DeCode(const int StepIndex);
};
class GeGroup
{
public:
GeGroup(HMap& m);
~GeGroup();
GeCode mGrop[NUM_GEGROUP];
GeCode mFitGes[NUM_GEGROUP];
int mBestGeIndex;
int mTotalFitSce;
HMap& mMap;
bool Epoch();
bool UpdateFitnessScore();
GeCode WheelSelect();
private:
int mGenerationCount;
static const int mMaxScore = NUM_MAX_GRID*NUM_MAX_GRID*4;
inline bool IsCrossOver(){return RandInt(0,10) <= RATE_CROSSOVER;}
inline bool IsMutation(){return RandInt(0,1000) <= RATE_MUTATION;}
int CalcuFitScore(const int idx);
int CalcuPosScore(const point& p);
void CrossOver(const int mom, const int dad);
void Mutate(const int geLst);
bool EvolutionSelection();
void ShowMarkMap();
};
#endif // _GEGROUP_HBB_
“GeGroup.cpp”
#include "GeGroup.h"
#include <cassert>
#include <iostream>
#include "HMap.h"
#include <stdlib.h>
using namespace std;
static const int MAX_INT = 0x7fffffff;
GeCode::GeCode():mHighestFitSce(0),mHighestSceStep(0)
{
}
GeCode::~GeCode(){}
SearchDir GeCode::DeCode(const int StepIndex)
{
int codePos = StepIndex*LEN_BIT_CODE;
unsigned Dir = 0;
Dir |= (1u&mGeRoute[codePos]) | ((1u&mGeRoute[codePos+1])<<1) | ((1u&mGeRoute[codePos+2])<<2);
return (SearchDir)Dir;
}
void GeCode::Clear()
{
for(int i = 0; i < LEN_BIT_ROUTE; ++i)
mGeRoute[i] = 0;
mHighestSceStep = 0;
mHighestFitSce = 0;
}
/*******************************
/// class GeGroup
*******************************/
GeGroup:: GeGroup(HMap& m):mBestGeIndex(0),mTotalFitSce(0)
,mMap(m),mGenerationCount(0)
{
for(int i = 0; i < NUM_GEGROUP;++i)
{
for(int j = 0; j<LEN_BIT_ROUTE;++j)
mGrop[i].mGeRoute[j] = RandInt(0,1);
}
}
GeGroup::~ GeGroup()
{
//dtor
}
int GeGroup::CalcuPosScore(const point& p)
{
// the longer distance, the lower score
return 10000/ (abs(p.x - mMap.Ep.x) + abs(p.y - mMap.Ep.y)+1);
}
// calculate the fitness score, and record the score of the last pos
int GeGroup::CalcuFitScore(const int idx)
{
assert(idx >=0 && idx < NUM_GEGROUP);
GeCode& geLst = mGrop[idx];
geLst.mHighestSceStep = 0;
geLst.mHighestFitSce = 0;
point curPos = mMap.Sp;
for(int i = 0; i < NUM_MAX_STEP;++i)
{
MvVector mv = gMvVctr[geLst.DeCode(i)];
curPos.x += mv.x;
curPos.y += mv.y;
geLst.mHighestSceStep = i;
// check,if wrond back to the previous pos
if(mMap.Map[curPos.x][curPos.y] == MAP_BLOCK || curPos.x<0
||curPos.x>=NUM_MAX_GRID||curPos.y<0||curPos.y>=NUM_MAX_GRID)
{
--geLst.mHighestSceStep;
break;
}
if(curPos == mMap.Ep)
{
geLst.mHighestFitSce = MAX_INT;
return MAX_INT;
}
}
geLst.mHighestFitSce = CalcuPosScore(curPos);
return geLst.mHighestFitSce;
}
bool GeGroup::UpdateFitnessScore()
{
mTotalFitSce = 0;
mBestGeIndex = 0;
int MaxSc = 0;
// update all the generic group fitness score
for(int i = 0; i < NUM_GEGROUP;++i)
{
int sce = CalcuFitScore(i);
if(sce == MAX_INT)
{
mBestGeIndex = i;
return true;
}
mTotalFitSce += sce;
if(MaxSc < sce)
{
MaxSc = sce;
mBestGeIndex = i;
}
}
return false;
}
GeCode GeGroup::WheelSelect()
{
// rand select out a generic vector
int JudgeVal = RandInt(0,mTotalFitSce);
int tScr = 0;
int Slcted = 0;
for(int i = 0;i<NUM_GEGROUP;++i)
{
tScr += mGrop[i].mHighestFitSce;
if(tScr >= JudgeVal)
{
Slcted = i;
break;
}
}
return mGrop[Slcted];
}
void GeGroup::CrossOver(const int mom, const int dad)
{
assert(mom>=0 && mom<NUM_GEGROUP && dad>=0 && dad<NUM_GEGROUP);
GeCode& geMom = mFitGes[mom];
GeCode& geDad = mFitGes[dad];
if(!IsCrossOver())
return;
// select a rand break pos on list
int brkPos = RandInt(1, LEN_BIT_ROUTE-1);
// exchange dad with mod from brkPos
for(int i = brkPos; i < LEN_BIT_ROUTE;++i)
{
bool val = geMom.mGeRoute[i];
geMom.mGeRoute[i] = geDad.mGeRoute[i];
geDad.mGeRoute[i] = val;
}
}
void GeGroup::Mutate(const int geLst)
{
assert(geLst>=0&&geLst<NUM_GEGROUP);
GeCode& ge = mFitGes[geLst];
for(int i = 0; i < LEN_BIT_ROUTE;++i)
{
if(IsMutation())
ge.mGeRoute[i] = !ge.mGeRoute[i] ; // mutation, reverse bit
}
}
// generation selection
bool GeGroup::EvolutionSelection()
{
// update score
if(UpdateFitnessScore())
return true;
// ShowMarkMap();
int ct = 0;
while(ct < NUM_GEGROUP)
{
// wheel select
mFitGes[ct] = WheelSelect();
mFitGes[ct + 1] = WheelSelect();
//crossover
CrossOver(ct,ct+1);
// mutate
Mutate(ct);
Mutate(ct+1);
ct += 2;
}
// copy val from fitnessGroup to geGroup
for(int i = 0;i<NUM_GEGROUP;++i)
{
mGrop[i] = mFitGes[i];
mFitGes[i].Clear();
}
return false;
}
// the core function
bool GeGroup::Epoch()
{
mMap.ShowMap();
bool hasFind = false;
while(++mGenerationCount < 1000)
{
// Sleep(100);
hasFind = EvolutionSelection();
// ShowMarkMap();
if(hasFind)
{
cout<<"Find the right way at generation: "<< mGenerationCount<<endl;
ShowMarkMap();
break;
}
}
// if find the way , mark the map
if(!hasFind)
cout<<"Can't find the way out!"<<endl;
return hasFind;
}
void GeGroup::ShowMarkMap()
{
GeCode& fitcode = mGrop[mBestGeIndex];
point curPos = mMap.Sp;
for(int i = 0; i<= fitcode.mHighestSceStep; ++i)
{
SearchDir curDir = fitcode.DeCode(i);
curPos.x += gMvVctr[curDir].x;
curPos.y += gMvVctr[curDir].y;
mMap.Map[curPos.x][curPos.y] = MAP_LOAD;
}
mMap.Map[mMap.Sp.x][mMap.Sp.y] = MAP_START;
mMap.Map[mMap.Ep.x][mMap.Ep.y] = MAP_END;
mMap.ShowMap();
// retire
curPos = mMap.Sp;
for(int i = 0; i<= fitcode.mHighestSceStep; ++i)
{
SearchDir curDir = fitcode.DeCode(i);
curPos.x += gMvVctr[curDir].x;
curPos.y += gMvVctr[curDir].y;
mMap.Map[curPos.x][curPos.y] = MAP_NONBLOCK;
}
mMap.Map[mMap.Sp.x][mMap.Sp.y] = MAP_START;
mMap.Map[mMap.Ep.x][mMap.Ep.y] = MAP_END;
}
地图类
主要是地图的初始化和显示功能:
“HMap.h”
#ifndef HMAP_H
#define HMAP_H
#include "Global.h"
struct MvVector
{
int x;
int y;
MvVector& operator=(const MvVector& mv)
{
x = mv.x;
y = mv.y;
return *this;
}
MvVector& operator+(const MvVector& mv)
{
x += mv.x;
y += mv.y;
return *this;
}
};
static const MvVector gMvVctr[8] =
{{0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1},{-1,0}, {-1,-1}};
class point
{
public:
int x;
int y;
point(int X=-1,int Y=-1):x(X),y(Y){};
point& operator=(const point& p)
{
x = p.x;
y = p.y;
return *this;
}
bool operator==(const point& pos)
{
return (x == pos.x) && (y == pos.y);
}
};
enum SearchDir // 8个搜索方向
{
Dir_LEFT = 0,
Dir_LEFT_DOWN,
Dir_DOWN,
Dir_RIGHT_DOWN,
Dir_RIGHT,
Dir_RIGHT_UP,
Dir_UP,
Dir_UP_LEFT,
};
class HMap
{
public:
char Map[NUM_MAX_GRID][NUM_MAX_GRID];
point Sp, Ep;
void InitMap();
void ShowMap();
HMap();
virtual ~HMap();
private:
void InitStartAndEndPoint();
static const point UP;
static const point DOWN;
};
#endif // HMAP_H
#include "HMap.h"
#include <iostream>
#include <time.h>
using namespace std;
HMap::HMap()
{
//ctor
}
HMap::~HMap()
{
//dtor
}
const point HMap::UP(0,0);
const point HMap::DOWN(NUM_MAX_GRID,NUM_MAX_GRID);
void HMap::InitMap()
{
srand((unsigned)time(NULL)); //可以改变随机数种子来改变地图情况
for(int i = UP.x; i < DOWN.x; ++i)
{
for(int j = UP.y; j < DOWN.y; ++j)
Map[i][j] = rand()&1?MAP_NONBLOCK:MAP_BLOCK; //将地图格子随机设为阻碍或非阻碍
}
InitStartAndEndPoint();
}
void HMap::InitStartAndEndPoint()
{
for(int i = 2; i >= 0; --i)
for(int j = 2; j >= 0; --j)
if(Map[i][j] != MAP_BLOCK)
{
Sp.x = i;
Sp.y = j;
cout<<"start: ( "<<i<<" , "<<j<<" )"<<endl;
Map[Sp.x][Sp.y] = MAP_START;
i = -1;
j = -1;
}
for(int i = NUM_MAX_GRID-1; i >= NUM_MAX_GRID-3; --i)
for(int j = NUM_MAX_GRID-1; j >= NUM_MAX_GRID-3; --j)
if(Map[i][j] != MAP_BLOCK)
{
Ep.x = i;
Ep.y = j;
cout<<"end: ( "<<i<<" , "<<j<<" )"<<endl;
Map[Ep.x][Ep.y]=MAP_END;
return;
}
}
// 打印地图情况
void HMap::ShowMap()
{
cout<<"START:( "<<Sp.x<<","<<Sp.y<<" )"<<endl;
cout<<"STOP:( "<<Ep.x<<","<<Ep.y<<" )"<<endl;
const char SPACE[8] = " ";
cout<<SPACE;
for(int i = UP.x; i < DOWN.x; ++i)
cout<<i<<SPACE;
cout<<endl;
for(int i = UP.x; i < DOWN.x; ++i)
{
cout<<i<<SPACE;
for(int j = UP.y; j < DOWN.y; ++j)
{
if(Map[i][j]!=MAP_BLOCK && Map[i][j]!=MAP_NONBLOCK)
{
#ifdef _WIN32
SetTextColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
#endif
cout<<Map[i][j]<<SPACE;
#ifdef _WIN32
SetTextColor(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
#endif // _WIN32
}
else
cout<<Map[i][j]<<SPACE;
}
cout<<endl<<endl;
}
cout<<endl<<"======================================================="
<<endl;
}
其它文件
“main”
#include <iostream>
#include "HMap.h"
#include "GeGroup.h"
using namespace std;
int main()
{
HMap amap;
while(1)
{
amap.InitMap();
GeGroup ge(amap);
ge.Epoch();
Sleep(2000);
}
}
“Global.h”
#ifndef _GLOBAL_HBB_
#define _GLOBAL_HBB_
#include <stdlib.h>
#include <stdio.h>
// 全局变量的定义
const int NUM_MAX_STEP = 20;
const int NUM_MAX_GRID = 10;
const char MAP_BLOCK = 'X';
const char MAP_NONBLOCK = '.';
const char MAP_LOAD = 'O';
const char MAP_START = 'S';
const char MAP_END = 'E';
// generic code for direction
const int LEN_BIT_CODE = 3; // 2^3 =8 direction
const int LEN_BIT_ROUTE = 120;//DirBitLen*MAX_SEARCH_NUM;
// generic group num
const int NUM_GEGROUP = 200;
// cross relative parameter
const int RATE_CROSSOVER = 7; // 10 is max
const int RATE_MUTATION = 10; // 1000 is Max
inline int RandInt(const int MinV, const int MaxV){return rand()%(MaxV-MinV+1) + MinV;}
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#include <conio.h>
#include "ConsoleUtils.h"
//default text colors can be found in wincon.h
inline void SetTextColor(WORD colors)
{
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, colors);
}
inline void PressAnyKeyToContinue()
{
printf("\n\nPress any key to continue\n");
while(!_kbhit()){}
return;
}
#else
#include <unistd.h>
#include <sys/time.h>
#define Sleep(s) sleep(s/1000)
#endif
#endif // _GLOBAL_HBB_
结果
寻路前的地图:
寻路后:
总结
上面的寻路计算到了19代, 花费的时间与A*寻路相比要长很多, 有时还会出现找不到路径的情况;
遗传算法在解决一些问题上是个不错的选择,但显然不适合用来寻路,但作为一个应用学习例子还是很不错的;