Pergunta

I am implementing something very similar to a Genetic Algorithm. So you go through multiple generations of a population - at the end of a generation you create a new population in three different ways 'randomly', 'mutation' and 'crossover'.

Currently the probabilities are static but I need to make it so that the probability of mutation gradually increases. I appreciate any direction as I'm a little stuck..

This is what I have:

int random = generator.nextInt(10);
if (random < 1)  
    randomlyCreate() 
else if (random > 1 && random < 9 )
    crossover(); 
else  
    mutate();

Thank you.

Foi útil?

Solução

In your if statement, replace the hard coded numbers with variables and update them at the start of each generation.

Your if statement effectively divides the interval 0 to 10 into three bins. The probability of calling mutate() vs crossover() vs randomlyCreate() depends on the size of each bin. You can adjust the mutation rate by gradually moving the boundaries of the bins.

In your code, mutate() is called 20% of the time, (when random = 9 or 1), randomlyCreate() is called 10% of the time (when random = 0) and crossover() is called the other 70% of the time.

The code below starts out with these same ratios at generation 0, but the mutation rate increases by 1% each generation. So for generation 1 the mutation rate is 21%, for generation 2 it is 22%, and so on. randomlyCreate() is called 1 / 7 as often as crossover(), regardless of the mutation rate.

You could make the increase in mutation rate quadratic, exponential, or whatever form you choose by altering getMutationBoundary().

I've used floats in the code below. Doubles would work just as well.

If the mutation rate is what you're most interested in, it might be more intuitive to move the mutation bin so that it's at [0, 2] initially, and then increase its upper boundary from there (2.1, 2.2, etc). Then you can read off the mutation rate easily, (21%, 22%, etc).

void mainLoop() {
    // make lots of generations
    for (int generation = 0; generation < MAX_GEN; generation++) {
        float mutationBoundary = getMutationBoundary(generation);   
        float creationBoundary = getCreationBoundary(mutationBoundary);
        createNewGeneration(mutationBoundary, creationBoundary);
        // Do some stuff with this generation, e.g. measure fitness
    }
}

void createNewGeneration(float mutationBoundary, float creationBoundary) {
    // create each member of this generation
    for (int i = 0; i < MAX_POP; i++) {
        createNewMember(mutationBoundary, creationBoundary);
    }
}

void createNewMember(float mutationBoundary, float creationBoundary) {
    float random = 10 * generator.nextFloat();

    if (random > mutationBoundary) {
        mutate();
    }
    else {
        if (random < creationBoundary) {
            randomlyCreate();
        }
        else {
            crossover();
        }
    }
}

float getMutationBoundary(int generation) {
    // Mutation bin is is initially between [8, 10].
    // Lower bound slides down linearly, so it becomes [7.9, 10], [7.8, 10], etc.
    // Subtracting 0.1 each generation makes the bin grow in size.
    // Initially the bin is 10 - 8 = 2.0 units wide, then 10 - 7.9 = 2.1 units wide,
    // and so on. So the probability of mutation grows from 2 / 10 = 20%
    // to 2.1 / 10 = 21% and so on.
    float boundary = 8 - 0.1f * generation;

    if (boundary < 0) {
        boundary = 0;
    }
    return boundary;    
}

float getCreationBoundary(float creationBoundary) {
    return creationBoundary / 8; // fixed ratio
}

Outras dicas

Use a variable where you are currently use the 9, and (for example) multiply that by 0.9 every itaration, unless mutate() happens, in which case you multiply it by 3 for example. that way the chance of mutation grows slowly but exponentially (yes, that is possible), until they actually mutate, at which point the chance of another mutation drops like a brick and the process starts all over again.

these values are completely random, and are not based on any knowledge about mutation whatsoever, but I'm just showing you with this how you could manipulate it to have a variable value every time. Also: if you use what I just used, make sure the value of the variable is set to 10 if it ever goes over 10.

Any choose of genetic probabilites for operators is arbitrary (also valid if you use some function for increasing or decreasing probabilities). Better to codify operators inside the chromosome. For example, you can add a number of bits to codify all operators you use. When generate children, you take a look to these bits for all elements of the population and apply the operator with a probability equal to the current situation of operators in the whole population, considered globally.

For example:

void adaptive_probabilities(GA *ga, long chromosome_length) {
    register int i, mut = 1, xover = 1, uxover = 1, ixover = 1, pop;
    char bit1, bit2;

    for (i = 0; i < ga->npop; i++) {
        bit1 = ga->pop[i]->chromosome[chromosome_length - 2];
        bit2 = ga->pop[i]->chromosome[chromosome_length - 1];

        if (bit1 == '0' && bit2 == '0') {
            mut++;
        } else if (bit1 == '0' && bit2 == '1') {
            xover++;
        } else if (bit1 == '1' && bit2 == '0') {
            uxover++;
        } else if (bit1 == '1' && bit2 == '1') {
            ixover++;
        }
    }

    pop = ga->npop + 4;

    ga->prob[0] = mut / (float)pop;
    ga->prob[1] = xover / (float)pop;
    ga->prob[2] = uxover / (float)pop;
    ga->prob[3] = ixover / (float)pop;
}

In my case I use two bits because my chromosomes codify for four operators (three types of crossover + mutation). Bits for operators are located to the end of chromosome. All probabilities are > 0 (counters for operators begin from 1) and then I have to normalize all probabilities correctly with

pop = ga->npop + 4;

Then, I generate a random number for choose the operator in base to the calculated probabilities saved in the array ga->prob.Last bits of new children are changed to reflect the operator used.

This mechanism ensures a double search by the GA: in error space (as usual) and in the operators space. Probabilites change automatically and are optimized because children are generated with higher probability using best operators at any moment of the calculation.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top