Оптимизация генетического алгоритма в C ++ - PullRequest
1 голос
/ 13 декабря 2010

Я ищу эффективные способы добавления или пропуска кода, чтобы моя программа генетического алгоритма могла быстрее возвращать результаты. Целью программы является принятие строки и создание других строк, которые максимально соответствуют друг другу. Какая бы новая строка не соответствовала ближайшему (верхнему 5) сопряжению с другими и производит потомство (некоторые из которых имеют мутации, которые помещают в строку новое случайное число без изменения длины). Все это прекрасно работает, но требуется немыслимое количество поколений, чтобы привести в соответствие некоторые из более длинных строк (от 4 и выше). Извините за длину tl; dr, но вот мой текущий код. Критика прочь!

    #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++;
        }   
}

Ответы [ 4 ]

5 голосов
/ 13 декабря 2010

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

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

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

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

0 голосов
/ 13 декабря 2010

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

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

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

0 голосов
/ 13 декабря 2010

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

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

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

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

0 голосов
/ 13 декабря 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...