Reputation: 197
I'm writing a genetic algorithm in which I need to select 5 numbers from the binary list genotype
and flip them, so a 1
→0
and 0
→1
. I tried putting my code in a loop with a range(1,6)
however when I do this it still only changes one of the numbers. Below is my original code without the loop which randomly selects one of the binary values and mutates it. Does anyone know a better way of doing this but for 5 of the elements in the list?
genotype = [1,0,0,1,0,0,1,1,1,0]
def mutate(self):
gene = random.choice(genotype)
if genotype[gene] == 1:
genotype[gene] = 0
else:
genotype[gene] = 1
return genotype
Upvotes: 4
Views: 459
Reputation: 154
Your question is more towards the software engineering problem, and not towards the EA side. But, I would like to give some suggestions towards your design decision of choosing 5 genes from the genome. You are restricting EA to explore the search space for your solutions, and your EA could definitely get stuck in local optima and might not be able to escape as mutated solutions will be changed around just 5 genes (If the limitation is from a problem itself, can't do anything about it). As an example, EA might find a near-optimal solution by just changing 3 genes or by changing 7 genes. If it is a design choice, you might like to revisit your design decision for EA.
In general, the EA community uses mutation probability for genes. When one would like to mutate offsprings, it is based on hyperparameter mutation probability (Also, as this is hyperparameter, you also can tune this for your problem to achieve good results later).
Let's say if your design decision of mutating 5 genes is a constraint, and also you would like to use tunable hyper-parameter mutation probability. You can have your solution like below:
import random
def mutate(genotype, m_probe):
mutated_offspring = []
mutated_genes_indexes = set()
for index, gene in enumerate(genotype):
if random.uniform(0, 1) >= m_probe and len(mutated_genes_indexes) < 5:
mutated_offspring.append(int(not gene))
mutated_genes_indexes.add(index)
else:
mutated_offspring.append(int(gene))
print("Mutated genes indexes: ", mutated_genes_indexes)
return mutated_offspring
# Each genes have 20% probability to get mutated! NOTE: with higher probability you might not find 5 genes mutated, 20 is chosen based on the constraint and can be tuned later with this constraint.
genotype = [1,0,0,1,0,0,1,1,1,0]
print(mutate(genotype, 0.20))
My preferred design decision would be an equal chance for all genes to get mutated without constraint. In that case, a solution might look like below:
import random
def mutate(genotype, m_probe):
mutated_offspring = []
mutated_genes_indexes = set()
for index, gene in enumerate(genotype):
if random.uniform(0, 1) >= m_probe:
mutated_offspring.append(int(not gene))
mutated_genes_indexes.add(index)
else:
mutated_offspring.append(int(gene))
print("Mutated genes indexes: ", mutated_genes_indexes)
return mutated_offspring
genotype = [1,0,0,1,0,0,1,1,1,0]
print(mutate(genotype, 0.50))
Upvotes: 1
Reputation: 10238
While your primary problem seems solved after the answer given by ilyankou:
for i in random.sample(range(len(genotype)), 5):
genotype[i] ^= 1
and this alternative (more realistic) mutation model was suggested:
for i in [random.choice(range(len(genotype))) for _ in range(5)]:
genotype[i] ^= 1
I found this observation quite challenging and somewhat inspiring
I tried putting my code in a loop with a range(1,6) however when I do this it still only changes one of the numbers.
Is this this always true? Can it or must it be?
I tried several runs of the following code (I removed the superfluous self
from your original)
import random
genotype = [1,0,0,1,0,0,1,1,1,0]
def mutate():
gene = random.choice(genotype)
if genotype[gene] == 1:
genotype[gene] = 0
else:
genotype[gene] = 1
return genotype
print(genotype)
for _ in range(1,6):
mutate()
print(genotype)
and observed only these outputs:
[0, 0, 0, 1, 0, 0, 1, 1, 1, 0]
-- gene at index 0 flipped[1, 1, 0, 1, 0, 0, 1, 1, 1, 0]
-- gene at index 1 flippedAnd indeed this has to be like this for an odd count of calls to the mutate
function above:
Because gene
is one of 0
and 1
and double flips on the same gene reproduce the initial value, only mutations will remain that correspond to gene indices which are chosen an odd number of times, and since you call it for range(1, 6)
(an odd total), only one of 0
and 1
can be of odd quantity in the overall process.
Upvotes: 2
Reputation: 1329
You can use random.sample()
function to get 5 unique indices from the list, and then flip them in a loop. Like that:
import random
genotype = [1,0,0,1,0,0,1,1,1,0]
random_five = random.sample(range(len(genotype)), 5)
for i in random_five:
genotype[i] = 0 if genotype[i] == 1 else 1
print(genotype)
Output is:
[1, 1, 0, 1, 1, 0, 0, 1, 0, 1]
Upvotes: 6
Reputation: 21
random.choice(genotype)
will return a random element from genotype list, ie, it will be equal to either 0 or 1. So, since you are using gene
as index, your function will always flip an element either at index 0, or at index 1.
You can use the random.sample(population, k)
function instead.
Upvotes: 2