接下来介绍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 | 适应值 | 百分比(%) | 累计百分比(%) | 选中次数 |
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 |
现采用轮盘赌方式选择个体,依次生成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;
}