您的位置:首页 > 大数据 > 人工智能

游戏编程中的人工智能技术-遗传算法入门(四)

2016-06-01 08:29 489 查看
接下来介绍SGenome结构体。

struct SGenome
{
vector<int> vecBits;

double		dFitness;

SGenome():dFitness(0){}

SGenome(const int num_bits):dFitness(0)
{
//create a random bit string
for (int i=0; i<num_bits; ++i)
{
vecBits.push_back(RandInt(0, 1)); //随机的产生一个染色体,vecBits.push_back表示往vecBits向量里塞值,RandInt(0, 1)表示塞的值要么是0,要么是1

}
}
};
定义了一个向量vecBIts,用来存储染色体中的每一位的。定义了一个dFItness,用来存储染色体的适应度函数值。
接下来就到了我们的正片环节:遗传算法类CgaBob了。

class CgaBob
{
private:

//the population of genomes
vector<SGenome>	m_vecGenomes; //定义一个向量m_vecGenomes,注意这个向量的每一个元素不是int型的了,是一个SGenome结构体,没错,每个元素都是一个结构体

//size of population
int             m_iPopSize;  //种群中染色体数

double          m_dCrossoverRate; //杂交率

double          m_dMutationRate; //变异率

//how many bits per chromosome
int             m_iChromoLength; //染色体的长度

//how many bits per gene
int             m_iGeneLength; //基因的长度

int             m_iFittestGenome; //染色体的适应度

double          m_dBestFitnessScore;//最佳染色体的适应度

double          m_dTotalFitnessScore;//所有染色体适应度总和

int             m_iGeneration;       //代数

//create an instance of the map class
CBobsMap        m_BobsMap;

//we use another CBobsMap object to keep a record of
//the best route each generation as an array of visited
//cells. This is only used for display purposes.
CBobsMap		m_BobsBrain;

//lets you know if the current run is in progress.
bool			m_bBusy;

void        Mutate(vector<int> &vecBits);

void        Crossover(const vector<int>	&mum,  //父类
const vector<int> &dad,<span style="white-space:pre">	</span>       //父类
vector<int>       &baby1,<span style="white-space:pre">	</span>//子类
vector<int>       &baby2);<span style="white-space:pre">	</span>//子类

SGenome&		RouletteWheelSelection(); //轮盘赌

//updates the genomes fitness with the new fitness scores and calculates
//the highest fitness and the fittest member of the population.
void			  UpdateFitnessScores();//适应度更新

//decodes a vector of bits into a vector of directions (ints)
vector<int>	Decode(const vector<int> &bits);

//converts a vector of bits into decimal. Used by Decode.
int				  BinToInt(const vector<int> &v);

//creates a start population of random bit strings
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(HWND hwnd);

void			Render(int cxClient, int cyClient, HDC surface);

void			Epoch();

//accessor methods
int				Generation(){return m_iGeneration;}
int				GetFittest(){return m_iFittestGenome;}
bool      Started(){return m_bBusy;}
void			Stop(){m_bBusy = false;}
};

#endif

以上是CgaBob类的介绍。对于CgaBob,关键的还是其中的函数。

函数1:void CgaBob::CreateStartPopulation()

void CgaBob::CreateStartPopulation()
{
//clear existing population
m_vecGenomes.clear();

for (int i=0; i<m_iPopSize; i++)
{
m_vecGenomes.push_back(SGenome(m_iChromoLength));
}

//reset all variables
m_iGeneration		 = 0;
m_iFittestGenome	 = 0;
m_dBestFitnessScore  = 0;
m_dTotalFitnessScore = 0;
}


CreateStartPopulation():产生初始种群。

for (int i=0; i<m_iPopSize; i++)
表示产生m_iPopSize个染色体。即产生一个种群。

m_vecGenomes:这个就是上述那个vector<SGenome> m_vecGenomes,所以里面存放的都是一个一个的SGenome。

m_vecGenomes.push_back(SGenome(m_iChromoLength)); 把一个一个的SGenome储存到m_vecGenomes中去,SGenome(m_iChromoLength)对应的是SGenome结构体定义中的

SGenome(const int num_bits):dFitness(0)
{
//create a random bit string
for (int i=0; i<num_bits; ++i)
{
vecBits.push_back(RandInt(0, 1));
}
}

表示长度为m_iChromoLength的随机染色体。

函数2:void CgaBob::Crossover( )

void CgaBob::Crossover( const vector<int> &mum,//父代1
const vector<int> &dad,//父代2
vector<int>		  &baby1,//子代1
vector<int>		  &baby2)//子代2
{
//just return parents as offspring dependent on the rate
//or if parents are the same
if ( (RandFloat() > m_dCrossoverRate) || (mum == dad)) //如果两个父代相同
{
baby1 = mum;//那么也就没有交叉的必要
baby2 = dad;

return;
}

//determine a crossover point
int cp = RandInt(0, m_iChromoLength - 1);//如果两个父代不相同,那么随机选择一个位。

//swap the bits
for (int i=0; i<cp; ++i)
{
baby1.push_back(mum[i]);//cp位之前,不变化
baby2.push_back(dad[i]);
}

for (i=cp; i<mum.size(); ++i)
{
baby1.push_back(dad[i]);//cp位之后,进行交叉操作
baby2.push_back(mum[i]);
}
}
此函数的作用是:将染色体进行交叉操作。

函数3:CgaBob::Mutate(vector<int> &vecBits)

void CgaBob::Mutate(vector<int> &vecBits)
{
for (int curBit=0; curBit<vecBits.size(); curBit++) 遍历染色体中的每个位
{
//do we flip this bit?
if (RandFloat() < m_dMutationRate)//随机选一个数,若这个数小于变异率的话(变异率通常很低很低)
{
//flip the bit
vecBits[curBit] = !vecBits[curBit]; 将该位取反
}
}//next bit
}
此函数的作用是:将染色体自身进行变异操作。

函数4:SGenome& CgaBob::RouletteWheelSelection()

SGenome& CgaBob::RouletteWheelSelection()
{
double fSlice	= RandFloat() * m_dTotalFitnessScore; //随机选一个数,当然这个数必须在适应度总和的范围之内,<span style="font-family: Arial, Helvetica, sans-serif;">RandFloat()是一个0~1之间的浮点数</span>

double cfTotal	= 0.0;

int	SelectedGenome = 0;

for (int i=0; i<m_iPopSize; ++i)//遍历种群中的每一个染色体
{

cfTotal += m_vecGenomes[i].dFitness;//适应度总和进行计算

if (cfTotal > fSlice) //轮盘赌,当总和大于随机数时
{
SelectedGenome = i;//表示这个染色体被选中
break;
}
}

return m_vecGenomes[SelectedGenome];
}
大名鼎鼎的轮盘赌来了!比较难以理解的是这句:

if (cfTotal > fSlice) //轮盘赌,当总和大于随机数时
{
SelectedGenome = i;//表示这个染色体被选中
break;
}
我们回到遗传算法入门(一),点击打开链接

再重温下那个轮盘赌的实例:

按照杨天齐书113页,初始种群情况表为:

编号个体串x适应值百分比(%)累计百分比(%)选中次数
S01011011316914.3014.301
S02110012562552.8867.182
S03010008645.4172.590
S04100101832427.411001
现采用轮盘赌方式选择个体,依次生成4个随机数为0.85、0.32、0.12、0.46。
以0.85为例,可以这样理解:只有S01时,累计百分比为14.30<0.85,因此不是S01;再加上S02,累计百分比为67.18<0.85,还不是S02;再加上S03,累计百分比为62.59<0.85,还不是S03;只有加上S04时,累计百分比100>0.85,因此选S04。

接下来的函数同样重要:void CgaBob::UpdateFitnessScores()

void CgaBob::UpdateFitnessScores()
{
m_iFittestGenome		= 0;
m_dBestFitnessScore		= 0;
m_dTotalFitnessScore	= 0;

CBobsMap TempMemory;

//update the fitness scores and keep a check on fittest so far
for (int i=0; i<m_iPopSize; ++i)
{
//decode each genomes chromosome into a vector of directions
vector<int> vecDirections = Decode(m_vecGenomes[i].vecBits);

//get it's fitness score
m_vecGenomes[i].dFitness = m_BobsMap.TestRoute(vecDirections, TempMemory);

//update total
m_dTotalFitnessScore += m_vecGenomes[i].dFitness;

//if this is the fittest genome found so far, store results
if (m_vecGenomes[i].dFitness > m_dBestFitnessScore)
{
m_dBestFitnessScore = m_vecGenomes[i].dFitness;

m_iFittestGenome = i;

m_BobsBrain = TempMemory;

//Has Bob found the exit?
if (m_vecGenomes[i].dFitness == 1)
{
//is so, stop the run
float a = m_vecGenomes[i].dFitness;
vector<int> b = m_vecGenomes[i].vecBits;
m_bBusy = false;
}
}

TempMemory.ResetMemory();

}//next genome
}
注释1:m_vecGenomes就是那个存放SGenome结构体的向量,每一个m_vecGenomes的元素用m_vecGenomes[i]代替,每一个元素又包含两个元素:vecBits向量和dFitness适应值。其中vecBits就是染色体,dFitness就是该染色体的适应值。

注释2:vector<int> vecDirections = Decode(m_vecGenomes[i].vecBits):将每一个m_vecGenomes元素中的vecBits提取出来,并将vecBits进行解码,即将原vecBits染色体破译为00、01、10、11的形式,即东、南、西、北。解码后的染色体用vecDirections向量表示。

注释3:m_vecGenomes[i].dFitness = m_BobsMap.TestRoute(vecDirections, TempMemory):将vecDirections的适应值计算出来,具体见m_BobsMap.TestRoute函数。

注释4:m_dTotalFitnessScore += m_vecGenomes[i].dFitness:计算适应度函数总数。

注释5:

if (m_vecGenomes[i].dFitness > m_dBestFitnessScore)//如果当前的适应度值为最优值,则将最优值替换,并且记录此染色体用作画图用
{
m_dBestFitnessScore = m_vecGenomes[i].dFitness;

m_iFittestGenome = i;

m_BobsBrain = TempMemory;//<span style="font-family: Arial, Helvetica, sans-serif;">记录此染色体用作画图用</span>

//Has Bob found the exit?
if (m_vecGenomes[i].dFitness == 1)//若适应度值到1了,就表示到终点了
{
//is so, stop the run
float a = m_vecGenomes[i].dFitness;//这个是我加的用来调试的(by qixing)
vector<int> b = m_vecGenomes[i].vecBits;//同样用来调试的(qx)
m_bBusy = false;//搞定
}
}
注释6:TempMemory.ResetMemory():画图用

最终的遗传算法流程void CgaBob::Epoch():

void CgaBob::Epoch()
{

UpdateFitnessScores();//适应值更新,这个放最后我感觉也可以吧

//Now to create a new population
int NewBabies = 0;

//create some storage for the baby genomes
vector<SGenome> vecBabyGenomes;

while (NewBabies < m_iPopSize)
{
//select 2 parents
SGenome mum = RouletteWheelSelection();//轮盘赌,选择父代1
SGenome dad = RouletteWheelSelection();<span style="font-family: Arial, Helvetica, sans-serif;">//轮盘赌,选择父代2</span>

//operator - crossover
SGenome baby1, baby2;
Crossover(mum.vecBits, dad.vecBits, baby1.vecBits, baby2.vecBits);//交叉

//operator - mutate
Mutate(baby1.vecBits);//变异
Mutate(baby2.vecBits);

//add to new population
vecBabyGenomes.push_back(baby1); //产生子代1
vecBabyGenomes.push_back(baby2);//产生子代2

NewBabies += 2; //+2后继续进行上述操作,依次结束后产生新的种群。
}

//copy babies back into starter population
m_vecGenomes = vecBabyGenomes;

//increment the generation counter
++m_iGeneration;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: