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

接下来介绍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;
}

    原文作者:遗传算法
    原文地址: https://blog.csdn.net/angelguard/article/details/51553240
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞