2010-12-12 75 views
1

我正在寻找添加或省略代码的有效方法,以帮助我的遗传算法程序返回更快的结果。该程序的目标是接受一个字符串并创建尽可能匹配的其他字符串。无论新制作的字符串是否与最接近的(前五名)匹配,并产生后代(其中一些突变会将新的随机数字放入字符串而不影响长度)。这一切都可以正常工作,但需要数不可思议的世代来获得一些较长的字符串(4以上)才能完美匹配。 对不起,关于tl; dr长度,但这是我现在的代码。批评!C++中的遗传算法优化

#include "stdio.h" 
    #include "fstream" 
    #include "ctime" 
    #include "iostream" 
    #include "string" 
    #include "windows.h" 

    #define CHARACTERS 16 
    #define STRINGS 100 
    /* 
    Enter String(max 16 chars) 
    Generate 100 words of the same length 
    Check for Fitness(how close each word is to the string) 
    Every generation: display top 5 
    Clone the top 5 
    Top 20 reproduce(mix each other's chars) 
    1/1000 chance the children might mutate(each newly mixed string or char might have a completely random number) 

    */ 

    typedef struct _stringHolder 
    { 
     char randString[CHARACTERS]; 
     int fitness; 
    }StringHolder; 


//Randomly generate 100 words 
void generate(char *myString, StringHolder *SH) 
{ 
    unsigned seed = time(0); 
    srand(seed); 
     //int i = 0; 
    int j = 0; 
    char randChar; 
     //char showString[CHARACTERS]; 
    for(int i=0; i<STRINGS; i++) 
    { 
     for(int j=0; j<strlen(myString); j++) 
     { 
      randChar = ('a' + (rand() %26)); 
      SH[i].randString[j] = randChar; 
     } 
     //limiter so that it doesn't crash 
     SH[i].randString[strlen(myString)] = 0; 
    } 
} 

//Check the similarity of the random strings to the original string. 
void getFitness(char *myString, StringHolder *SH) 
{ 
    for(int i=0; i<STRINGS; i++) 
    { 
     for(int j=0; j<strlen(myString); j++) 
     { 
      if(SH[i].randString[j] == myString[j]) 
      { SH[i].fitness++; } 
     } 
    } 
} 

//Sort the strings 
void sortByFitness(char *myString, StringHolder *SH) 
{ 

     bool swapped = 1; 
     while(swapped) 
     { 
      swapped = 0; 
      for(int a=0; a<STRINGS-1; a++) 
      { 
       if(SH[a].fitness < SH[a+1].fitness) 
       { 
        swapped = 1; 


         StringHolder temp[STRINGS]; 
         temp[a] = SH[a+1]/*.randString[i]*/; 
         SH[a+1]/*.randString[i]*/ = SH[a]/*.randString[i]*/; 
         SH[a]/*.randString[i]*/ = temp[a]; 

        /*if(SH[a].fitness < SH[a+1].fitness) 
        { swapped = 0; }*/ 
       } 
      } 
     }//while 
} 

//Clone the Top 5 strings 
void cloneTopFive(char *myString, StringHolder *SH, StringHolder *cloneString) 
{ 
    for(int i=0; i<5; i++) 
    {  
      cloneString[i]/*.randString[j]*/ = SH[i]/*.randString[j]*/; 
      //printf("cloneString[%d] now holds %s.\n", i, SH[i].randString); 

    } 
} 
//Reproduce the Top 20 strings by mixing and matching elements between strings 
void reproduceTopTwenty(char *myString, StringHolder *SH /*char *cloneString*/) 
{ 
    /*for(int h=5; h<95; h++) 
    {*/ 
     for(int i=0; i<20; i++) 
     { 
      for(int j=0; j<strlen(myString)-1; j++) 
      { 
       //char temp[16]; 
       //temp[i] = 
       SH[i].randString[j] = SH[1 + (rand() %20)].randString[1 + (rand() %strlen(myString)-1)]; 
       int randomNumber; 
       randomNumber = (1 +(rand() %100)); 
       if(randomNumber == 7) 
       { 
        SH[i].randString[1 + (rand() %strlen(myString)-1)] = ('a' + (rand() %26)); 
       } 
      } 
     } 

} 
//Randomize the other 75 numbers and place the cloned Top 5 at the end of the String Holder(SH) 
void randomizeOther75(char *myString, StringHolder *SH, StringHolder *cloneString) 
{ 
    for(int i=20; i<STRINGS; i++) 
    { 
     for(int j=0; j<strlen(myString); j++) 
     { 
      SH[i].randString[j] = ('a' + (rand() %26)); 
     } 
    } 

    for(int i=0; i<5; i++) 
    { 
     for(int j=0; j<strlen(myString); j++) 
     { 
      int v = i + 94; 
      SH[v].randString[j] = cloneString[i].randString[j]; 
     } 
    } 

} 
void printGen(char *myString, StringHolder *SH) 
{ 
    for(int i=0; i<5; i++) 
     {  
      if(SH[i].fitness == strlen(myString)) 
      { printf("%s has %d fitness. Perfection!\n", SH[i].randString, SH[i].fitness); } 
      else 
      printf("%s has %d fitness.\n", SH[i].randString, SH[i].fitness); 
     } 
} 
void main() 
{ 
    char myString[CHARACTERS]; 
    StringHolder cloneString[5]; 
    StringHolder SH[STRINGS]; 
    for(int i=0; i<STRINGS; i++) 
    { SH[i].fitness = 0; } 

    printf("Enter your name(no whitespaces): "); 
    scanf("%s", myString); 
    /*while(strlen(myString) >= CHARACTERS) 
    { 
     printf("Please type a string with less than 16 characters\n"); 
     scanf("%s", myString); 
    }*/ 
    //printf("%s\n", myString); 

    //first generation 
    generate(myString, SH); 
    int gen = 0; 
    while(1) 
    { 
     char x = ' '; 
    /* printf("Insert something. Anything!"); 
     scanf(&x);*/ 


     /*char newString[CHARACTERS]; 
     for(int i=0; i<5; i++) 
     { 
      for(int j=0; j< strlen(myString); j++) 
      {   
       newString[j] = SH[i].randString[j]; 
      } 
      newString[strlen(myString)] = 0; 
      printf("%s has %d fitness.\n", newString, SH[i].fitness); 
     }*/ 

     printf("\n"); 
     while(x==' ') 
     { 
      printf("Generation %d: \n", gen); 
      getFitness(myString, SH); 
      sortByFitness(myString, SH); 

      printGen(myString, SH); 

      for(int i=0; i<STRINGS; i++) 
      { SH[i].fitness = 0; } 

      cloneTopFive(myString, SH, cloneString); 
      reproduceTopTwenty(myString, SH); 
      randomizeOther75(myString, SH, cloneString); 
      /*getFitness(myString, SH); 
      sortByFitness(myString, SH); 

      for(int i=0; i<5; i++) 
      { 
       printf("%s has %d fitness.\n", SH[i].randString, SH[i].fitness); 
      } 
      printf("\n");*/ 

      //printf("\nInsert ' ' to continue!\n"); 

      //scanf("%c",&x); 
      gen++; 
     } 
} 

回答

0

对于每个人来说,算法的大部分领域(如健身评估)都可以独立执行。对于一些非常好的加速,我建议并行执行这些,CUDA是一个很好的体系结构。

+0

我希望我知道更多能够做像CUDA的东西。我仍然是一个新手程序员。确切地说, – 2010-12-13 00:02:32

0

不幸的是,遗传算法的本质意味着有时你只需要调整参数,看看你是否能够更快地找到解决方案。尝试克隆排名前10位的个人,或前7位或前3位。将前20位改为(例如)50.增加或减少突变率。

令人遗憾的是,我们还没有充分了解GA能够在没有这种调整的情况下确定“正确”的参数。

代码优化是一个完全独立的问题,可以使每一代运行得更快,但我怀疑你遇到的问题是它需要太多的时代,所以我不会说这个问题。

+0

。我的意思是减少获得“完美”答案所需的世代数量。 – 2010-12-13 00:04:58

+0

有一个downvote?为什么? – 2010-12-13 22:21:17

+0

如果您认为答案不对,请解释。它会帮助每个人! – 2010-12-13 22:27:16

0

您需要查看GA的参数。对于这样简单的计算,你的人口太小了。如果不是10或100K,你不应该遇到任何麻烦,至少要达到1000。你根本没有足够的解决方案来快速收敛到一个好的结果。

此外,您的精英主义(您为下一代克隆的候选人数量)相当高。你通常不希望超过2%的精英主义。

你也可以看看你是如何做你的交叉功能。你通常希望为整个人群进行交叉,而不是仅仅排在前20%。将所有95个未克隆的值传递到交叉函数中,您将看到人口中的多样性。正如Cameron所说,你的问题可能在于你的参数而不是你的代码,这是一个完全不同的问题,但这应该可以帮助你。祝你好运!

+0

事实上,我被专门分配到前20名。 – 2010-12-13 00:08:57

+0

大量人口*可能会有所帮助,但也值得关注小群体演化算法的大量文献。你能举出2%的参考吗?这是另一个必须调整的参数。 2%有时会起作用,0.2%或20%会在其他情况下起作用。我同意“跨越一切”一点。作为一个广告:看看斯金纳*等*关于使用突变来寻找新的遗传材料:) – 2010-12-13 22:20:36

+0

该参考文献来自M.M.Peterson和F.Moore博士的工作,两人原本都是赖特状态,以及来自我自己的EC研究的个人经验。这可能有点罗嗦,但值得读取国际海事组织。正如你所指出的那样,这不是一条硬性规则,它就像调整任何其他控制器一样,但我不记得我用过的GA,在那里我通过增加精英主义获得了积极的表现。 – Raskolnikov 2010-12-13 22:31:54

5

遗传算法收敛较差的一个重要原因是您的健身功能。忽略程序其他部分的潜在编码错误,你所做的只是奖励完美匹配的字母。健身风景是这样的(恐惧我的ASCII艺术!):

 
___________ ___________ 
      | | 
      |_| 
a b c d e f G h i j k l m 

其中G是所需的字母。该算法不知道如何找到G,但通过纯粹的运气。你基本上已经实施了一个随机的字母明智的蛮力搜索。

使适应度函数奖励“接近”正确的解决方案,收敛速度会快得多。还调整人口参数,突变,交叉等

+0

“使健身功能回报”贴近“正确的解决方案,收敛速度会快得多”:不一定......它取决于您的操作员是否具有相同的“距离”概念。从代码中,我不相信他们这样做。如果变异算子改变为从字符值中加上或减去一个,那么是的,这个答案是正确的。如果突变选择一个新的随机值,那么这个答案是不正确的。 GAs很棘手! – 2010-12-13 22:17:05

+0

通过仔细阅读代码,我越来越相信这会*不工作。交叉选择来自随机的父等位基因,并且突变本质上是不存在的(相反,底部75个个体是随机产生的)。没有任何机制可以在单个等位基因上做任何类型的梯度下降,所以以这种方式改变适应度函数基本上什么都不会做。我不会因为*观点*好而失望,但在这种情况下,它也需要对遗传操作者进行修改。 – 2010-12-13 22:30:53

+0

我必须同意答案和评论。根据与原始距离的距离调整你的健身功能,而不是仅仅考虑汉明距离,这可能会有所帮助,不过这意味着改变你的变异函数来增加或减少一个角色的数量,而不是随便选择另一个角色。 – Raskolnikov 2010-12-13 22:33:45