AI中的几种搜索算法---基因算法

引言算法

进化计算(Evolutionary Computation)这个涵盖的范围比较广,其中包括基因算法(Genetic Algorithm)、进化式策略(Evolutionary Strategy)、基因程序(Genetic Programming)等等。这篇是进化计算的开篇,我会从基因算法入手,进而介绍进化计算中的一些基本思想。学习

1、基因算法的基本介绍

1.核心思想

基因算法与A*、Tabu、BFS等一些启发式算法,最大的不一样即是:从针对个体,转变到针对由个体组成的“群体”(Population)。根据适应值(Fitness)来决定个体的优秀程度。code

每一次操做,从群体中挑选两个优秀的个体,取出这两个个体的基因,进行拆分重组,从而获得新方案,放入新一代的群体中去。ci

其中利用到了生物学中的重组(Recombination)、选择(Selection)和突变(Mutation)。因此在学习这一算法的时候,不妨和生物学中一些概念进行类比,这样可以更好地理解基因算法的工做原理。资源

基因算法,在挑选的过程当中,随机地挑选了两个优秀个体;在对两个个体的基因重组的时候,也引用了突变这一个不肯定因素,整个过程貌似都笼罩在“随机”阴影下。确实在某种意义上,基因算法是一种随机搜索的算法。但必须指出的是基因算法在搜索能力上大大优于普通的随机搜索。it

2.基因拆分重组

接下来介绍一些经常使用的基因重组算法。io

通常来讲,包括这三个操做:交叉(Crossover)、突变Mutation和倒置(Inversion)。原理

接下来我解释这三个操做:select

  1. 交叉

ParentA(1111111111111111) ParentB(0000000000000000)搜索

Crossover

ChildA(1111111100000000) ChildB(0000000011111111)

就像上面,将ParentA切成两段,同时也将ParentB的基因链也切成两段。

先将ParentB的一半基因连接在ParentA的一半基因链后面,从而产生ChildA;同理可得ChildB,只是交换了ParentA和ParentB的顺序。

  1. 突变

ParentA(1111111111111111)    ParentB(0000000000000000)

                   Mutation

ChildA(1111111101111111)     ChildB(0000000000100000)

这里ChildA从ParentA中获得了所有的基因,可是能够发现,其中ChildA中有一位是0,而0显然不是ParentA的基因,因此这即是突变。

同理可得ChildB。

  1. 倒置
ParentA(1111111111111111)    ParentB(0000000000000000)

                   Inversion

ChildA(1111111100011111)     ChildB(0000000111100000)

这个操做的结果有点相似突变。其实这个的操做过程是这样的:去ParentA的一个片断,对这个片断中每个基因进行取反,从而获得了ChildA。

3.基本流程

基因算法的流程也是简单易懂的,接下来就大体描述一下这个流程:

  1. 首先即是建立群体,不断随机建立个体。

  2. 从当前群体中挑选两个优秀个体,对其基因进行重组,生成两个新个体,这两个新个体便组成了新一代的群体。

  3. 利用步骤2,建立一个与当前群体容量至关的新一代群体,便将新一代群体设定为当前群体。

  4. 判断是否获得了咱们所须要的个体,若是获得就中止算法。

  5. 判断群体是否再也不符合要求(失去了多样性),若是不符合就中止算法;若是符合就继续步骤3。

伪代码:

Population[2][NUM];  
//随机得到NUM个个体,并放入群体Population[0]中去  
Rand(Population[0]);  
curGeneration = 0;  
While(TRUE)  
{  
newGeneration = curGeneration == 1 ? 0 : 1;  
   For(i = 0 ; i < NUM ; ++i)  
{  
   //select : 从当前群体Population中挑选优秀的个体做为这次操做的父辈  
   ParantA =  Select(Population[curGeneration]);  
   ParentB =  Select(Population[curGeneration]);  
       //Recombination: 重组ParentA和ParentB的基因链,得到新个体Child  
   Child  =  Recombination(ParentA,ParentB);      
//计算新一代个体的适应值  
   CalculateFitness(Child);  
   //若是得到Child并不比父辈的优秀,从新重组  
    If(Child.Fitness< ParentA.Fitness || Child.Fitness < ParentB.Fitness)  
    {  
      --i;  
      Continue;  
}  
//将新个体加入新一代的群体中  
    Population[newGeneration][i]= Child;  
}  
//当群体之间的个体差别很小的时候,考虑退出算法  
If(avgfitness / maxfitness > 0.99999)    
 Break;  
curGeneration = newGeneration;  
}

2、TSP问题

1.TSP问题介绍

这里举TSP问题,TSP问题在另外一篇文章《AI中的几种搜索算法---SA搜索算法》有提到过,那个时候主要用的是SA算法对这个问题进行了求解。

这里咱们会用基因算法,再次求解这个问题。虽然已经介绍过TSP问题,这里为了阅读的方便,我就直接拷贝了《AI中的几种搜索算法---SA搜索算法》的部份内容。

TSP问题即旅行商问题:一个旅行商A被分配到一个任务,公司要求A去几个城市进行公司业务拓展,因此A就会拿出地图制定一个合理的路线。其中路线的要求即是消耗最小,而且可以从某一个城市出发,而且最后返回该城市时,已经访问过了全部城市。

2.TSP问题分析

这里咱们能够计算出整个路线的路程。而这个路程和咱们以前提到过的Fitness成反比,路程越长,表示这个路线越差。

对于这个公式,我稍做解释:Fitness就是咱们一直提到的适应能力(适应值),Length(solution)计算路线solution的路程长度。

  1. 杂交算子

这里咱们介绍一个新的基因重组算法。较之于以前介绍的“交叉”、“突变”和“倒置”,这个算法会复杂一点。

杂交算子,算法来于《构建“基因库”求解TSP问题的混合遗传算法》

接下来开始介绍:

  1. 首先从当前群体中,随机选取两个优秀的个体做为父辈:ParentA和ParentB。

  2. 随机选取两个基因位置(即处于基因链中第几个位置):PosA1和PosA2。

  3. 找到ParentA基因链,PosA1和PosA2位置处的基因:G1和G2。

  4. 找到ParentB基因链中基因G1和G2所处的位置:PosB1和PosB2。

  5. 将ParentB基因链中,与处于ParentA基因链PosA1和PosA2之间相同的基因去掉。

  6. 而后若是PosB1 < PosB2,将处于ParentA基因链PosA1和PosA2之间的基因片断,在ParentB基因链的PosB1位置开始顺序插入;

若是PosB1 >= PosB2,将将处于ParentA基因链PosA1和PosA2之间的基因片断,在ParentB基因链的PosB2位置开始逆序插入。

  1. 通过步骤6以后,获得新个体Child,若是Child并无比父辈优秀,则再回到步骤1,继续相同操做;若是新个体优于父辈,则将新个体放入新一代群体中,再继续产生下一个个体。

下面举一个具体的例子

随机取PosA1 = 3 , PosA2 = 6 , 获得基因G1 = 3 , G2 = 6

在ParentB基因链的位置PosB1 = 8 , PosB2 = 5

ParentA :  1    2    3     4    5    6    7    8    9

ParentB:   2    4   7     8    6    5   1     3    9


将ParentB基因链,去除基因 3,4,5,6

获得:

ParentB:  2    x    7    8    x    x   1     x    9

 

由于PosB1 > PosB2,因此在PosB2处开始逆序插入(3,4,5,6)

获得:

Child:    2    7    8     6    5    4    3     1   9
  1. 代码

若是想要详细了解能够去基因算法解决TSP问题处下载

下面是整个基因算法的流程代码:

int tsp_ga(City * cities,intnCities,int ** path)  
{  
    srand(time(NULL));//  
    *path = new int[nCities];  
    floatsumCurFitness ;  
//初始化群体,随机获得一群个体  
    InitPopulation(cities,nCities,sumCurFitness);  
    intcurGeneration = 0,generation = 0;  
    int iBest =0;  
    for(;1 ;++generation)//循环  
    {  
         int newGeneration = curGeneration == 0 ? 1 : 0;  
         float sumFitness = 0.0 , maxFitness = 0.0;  
          //开始得到新个体  
         for (int i = 0 ; i< NUMPOPULATION ; ++i)  
         {  
              //选取优秀个体做为父辈  
             int parenta =SelectParent(curGeneration,sumCurFitness);  
             int parentb =SelectParent(curGeneration,sumCurFitness);  
            //开始重组父辈的基因链,得到新个体  
             //这里使用上面介绍的杂交算法  
 Recombination(population[curGeneration][parenta],population[curGeneration][parentb],  
                 population[newGeneration][i],nCities);  
              //计算新个体的fitness  
             CaculateFitness(cities,nCities,population[newGeneration][i]);  
             float fitness= population[newGeneration][i].fitness;  
              //若是新个体并无比父辈优秀,从新产生新个体  
             if(fitness< population[curGeneration][parenta].fitness || fitness <population[curGeneration][parentb].fitness)  
             {  
                 --i;  
                 continue;  
             }  
             if(fitness> maxFitness)  
             {  
                 maxFitness = fitness;  
                 iBest = i;  
             }  
             sumFitness += fitness;  
         }  
         sumCurFitness = sumFitness;  
         curGeneration = newGeneration;  
         float result = sumFitness / (maxFitness*NUMPOPULATION );  
         if( result >  0.99999)//若是群体中个体差别很小,中止算法  
             break;  
    }  
    for (int i = 0 ; i < nCities ; ++i)  
    {  
        (*path)[i] =population[curGeneration][iBest].path[i];  
    }  
     
    //清理资源  
    for (int i  = 0;  i < NUMPOPULATION ; ++i)  
    {  
        delete[]population[0][i].path;  
        delete[]population[1][i].path;  
    }  
    returngeneration;  
}  
//下面就是基因链重组算法的实现代码
void Recombination(constIndividual & parenta,const Individual &parentb,Individual & child,int n)  
{  
    int posa1 =rand()%(n-1);  
    int posa2 =rand()%n;  
    while(posa2<= posa1)  
        posa2 = rand()%n;  
    //  
    int lseg =posa2 - posa1 + 1;  
    int * genseg= new int[lseg];  
    for(int i = posa1 ; i <= posa2 ; ++i)  
        genseg[i-posa1] = parenta.path[i];  
    ////  
    int posb1 =-1,posb2 = -1;  
    int iChild =0 , iStartInsert = 0;  
    for(int i = 0 ; i < n ; ++i)  
    {  
        int gb =parentb.path[i];  
        int j =0;  
        for(; j< lseg; ++j)  
        {  
            intg = genseg[j];  
            if(gb== g)  
            {  
                if(0== j)  
                {  
                    posb1 = i;  
                    iStartInsert = iChild;  
                    iChild += lseg;  
                }  
                elseif(j == (lseg-1))  
                    posb2 = i;  
                break;  
            }  
        }  
        //  
        if(lseg== j)  
        {  
            child.path[iChild++] = gb;  
        }  
    }  
    if(posb1< posb2)  
        for(int i = 0 ; i < lseg ; ++i)  
            child.path[i + iStartInsert] =genseg[i];  
    else  
        for(int i = 0 ; i < lseg ; ++i)  
            child.path[i + iStartInsert] =genseg[lseg - i -1];  
    delete[]genseg;  
}
  1. 效果图

3、总结

基因算法总的来讲体现了一个“优胜劣汰”的法则,优秀的基因存活下来。并且基因算法从针对于个体转到了群体,有别于A*这些普通的启发式算法。

其中我在实现这个算法的时候,在尝试基因链重组算法的时候,一直没有找到一个可以保证优秀基因遗传下去的好方法,所在在网上搜了一下关于TSP和基因算法,找到了一篇论文《构建“基因库”求解TSP问题的混合遗传算法》,有兴趣的读者能够去看一下这篇文章。

若是有兴趣的能够留言,一块儿交流一下算法学习的心得。

相关文章
相关标签/搜索