游戏编程中的人工智能技术-遗传算法入门(四)
2016-06-01 08:29
489 查看
接下来介绍SGenome结构体。
接下来就到了我们的正片环节:遗传算法类CgaBob了。
以上是CgaBob类的介绍。对于CgaBob,关键的还是其中的函数。
函数1:void CgaBob::CreateStartPopulation()
CreateStartPopulation():产生初始种群。
m_vecGenomes:这个就是上述那个vector<SGenome> m_vecGenomes,所以里面存放的都是一个一个的SGenome。
m_vecGenomes.push_back(SGenome(m_iChromoLength)); 把一个一个的SGenome储存到m_vecGenomes中去,SGenome(m_iChromoLength)对应的是SGenome结构体定义中的
表示长度为m_iChromoLength的随机染色体。
函数2:void CgaBob::Crossover( )
函数3:CgaBob::Mutate(vector<int> &vecBits)
函数4:SGenome& CgaBob::RouletteWheelSelection()
再重温下那个轮盘赌的实例:
按照杨天齐书113页,初始种群情况表为:
现采用轮盘赌方式选择个体,依次生成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()
注释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:
最终的遗传算法流程void CgaBob::Epoch():
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 | 适应值 | 百分比(%) | 累计百分比(%) | 选中次数 |
S01 | 01101 | 13 | 169 | 14.30 | 14.30 | 1 |
S02 | 11001 | 25 | 625 | 52.88 | 67.18 | 2 |
S03 | 01000 | 8 | 64 | 5.41 | 72.59 | 0 |
S04 | 10010 | 18 | 324 | 27.41 | 100 | 1 |
以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; }
相关文章推荐
- 关于人工智能的幻想(九)第三部分
- 游戏编程中的人工智能技术-遗传算法入门(三)
- Hard-题目47:336. Palindrome Pairs
- 区间动规--区分两道题--【cqoi】涂色&【cdoi】string painter
- 游戏编程中的人工智能技术-遗传算法入门(二)
- RAII(资源创建即释放)
- root.sh Fails to Start HAIP as Default Gateway is Configured for Private Network VLAN (文档 ID 1366211
- HDU 2653 - Waiting ten thousand years for Love
- LeetCode 70. Climbing Stairs
- 3.INSTALL_FAILED_UPDATE_INCOMPATIBLE
- main Runloop
- 游戏编程中的人工智能技术-第三章:遗传算法入门(一)
- Seaborn-PairGrid
- grails指定端口运行工程
- [LeetCode]problem 70. Climbing Stairs
- HDOJ--Fibonacci Again
- Middle-题目122:220. Contains Duplicate III
- 1079. Total Sales of Supply Chain (25)
- leetcode 217. Contains Duplicate
- 1282 - Leading and Trailing ---LightOj1282(快速幂 + 数学)