2010-12-12 2 views
1

Я ищу эффективные средства добавления или исключения кода, чтобы помочь моей программе генетического алгоритма быстрее получить результаты. Цель программы - принять строку и создать другие строки, которые будут соответствовать как можно ближе. Какая бы ни была сделанная строка соответствует ближайшему (верхнему 5) помощнику с другими и дает потомство (некоторые из которых имеют мутации, которые помещают новое случайное число в строку, не влияя на длину). Все работает отлично, но для получения более длинных строк (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. Я все еще больше начинающий программист. –

0

К сожалению, природа генетических алгоритмов означает, что иногда вам просто нужно настроить параметры и посмотреть, сможете ли вы быстрее найти решение. Попробуйте клонировать 10 лучших лиц или верхнюю часть 7 или верхнюю часть 3. Измените верхнюю 20 на (например,) 50. Увеличьте или уменьшите скорость мутации.

К сожалению, мы еще недостаточно разбираемся в GA, чтобы иметь возможность определять «правильные» параметры без такой настройки.

Оптимизация кода - это целый отдельный вопрос, который может заставить каждое поколение работать быстрее, но я подозреваю, что проблема заключается в том, что он занимает слишком много поколений, поэтому я не буду говорить с этим.

+0

Точно. Я хотел уменьшить количество поколений, необходимых для получения «идеального» ответа. –

+0

Был ли нижний предел? Зачем? –

+0

Если вы считаете, что ответ неверен, пожалуйста, объясните. Это поможет всем! –

0

Вам необходимо посмотреть параметры вашей ГА. Для таких простых вычислений ваше население слишком мало. У вас не должно быть никаких проблем, если вы столкнетесь с ней, по крайней мере, до 1000, если не 10 или 100K. У вас просто недостаточно решений в пуле, чтобы быстро сходиться к хорошему результату.

Кроме того, ваш элитарность (количество кандидатов, клонов для следующего поколения) довольно велико. Обычно вы не хотите превышать 2% за элитизм.

Вы также можете посмотреть, как вы выполняете функцию кроссовера. Обычно вы хотите выполнить кроссовер для всего населения, а не только топ-20%. Передайте все 95 ваших не клонированных значений в свою функцию кроссовера, и вы увидите больше разнообразия в своем населении.

Как сказал Кэмерон, ваши проблемы скорее всего лежат в ваших параметрах, чем в коде, и это совершенно другая проблема, но это должно помочь вам на вашем пути. Удачи!

+0

Дело в том, что я был специально назначен иметь Top 20. –

+0

Большая популяция * может * помочь, но стоит посмотреть на богатство литературы по эволюционным алгоритмам небольшого населения. Не могли бы вы привести ссылку на 2%? Это еще один параметр, который необходимо настроить. 2% будут работать иногда, 0,2% или 20% будут работать в других случаях. Я согласен с «крестом все». В качестве откровенной рекламы: взгляните на Скиннера * и др. * В отношении использования мутации, чтобы найти новый генетический материал :) –

+0

Ссылка взята из работ д-ром М. Петерсоном и Ф. Муром, оба изначально из Райта Государства, а также личный опыт моего собственного исследования в области ИС. Это, наверное, немного многословие, но стоит прочитать ИМО. И, как вы отмечаете, это не сложное и быстрое правило, это точно так же, как настройка любого другого контроллера, но я не могу вспомнить GA, который я использовал, где я получил положительную производительность, увеличив элитарность выше этого. – Raskolnikov

5

Одна из главных причин, по которой GA плохо сходится, - это ваша фитнес-функция. Невзирая на возможные ошибки кодирования в других частях программы, то, что вы делаете, вознаграждает только идеально подобранные буквы. Подходит фитнес-ландшафт (страх моего искусства ASCII!):

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

Где G - желаемая буква. Алгоритм не имеет понятия, как найти G, но благодаря чистой удаче. Вы в основном реализовали случайный поиск грубой силы по буквам.

Сделать функцию фитнес-вознаграждения «близостью» к правильному решению, а конвергенция будет намного быстрее. Также настройте параметры популяции, мутацию, кроссовер и т. Д.

+0

«Сделать функцию фитнес-функции« близостью »к правильному решению, а конвергенция будет намного быстрее»: не обязательно ... это зависит от того, имеют ли ваши операторы одинаковые понятия «расстояние» или нет. Из кода я не уверен, что они это делают. Если оператор мутации изменяется, чтобы добавить или вычесть его из символьного значения, тогда да, этот ответ правильный. Если мутация выбирает новое случайное значение, этот ответ неверен. GA сложны! –

+0

Из более подробного чтения кода я все больше убеждаюсь, что это будет * не * работать. Кроссовер выбирает из случайных родительских аллелей, а мутация по существу не существует (скорее, дно 75 человек генерируются случайным образом). Не существует механизма, позволяющего выполнять какой-либо градиентный спуск на одном аллеле, поэтому изменение функции фитнеса таким образом практически ничего не даст. Я не буду понижать, потому что идея * хороша, но в этом случае она также нуждается в модификации для генетических операторов. –

+0

Я должен согласиться с ответом и комментарием. Тренировка вашей фитнес-функции для вознаграждения, основанная на дистанции от оригинала, а не просто на расстоянии Хэмминга, может помочь, хотя это будет означать изменение вашей функции мутации, чтобы увеличивать или уменьшать характер на небольшую величину, а не просто выбирать другого персонажа случайным образом. – Raskolnikov