3
\$\begingroup\$

I haven't done any research into how you actually write a genetic algorithm, so this is my best guess. What I'm really curious to know is two-fold:

  1. Have I created a genetic algorithm?
  2. If I have, how can I continue to explore the subject more?

I hope this code and docstrings are enough explanation for how the code works.

from random import randint
import time
class Organism(object):
 def __init__(self, r=0, g=0, b=0):
 """Initialize the class with the RGB color values."""
 self.r = r
 self.g = g
 self.b = b
 @property
 def fitness(self):
 """The lower the fitness the better."""
 total = self.r + self.g + self.b
 return abs(765 - total)
 @classmethod
 def spawn(cls, parent):
 """Return a mutated generation of ten members."""
 generation = []
 for number in range(0, 10):
 r = cls.mutate(parent.r)
 g = cls.mutate(parent.g)
 b = cls.mutate(parent.b)
 generation.append(cls(r, g, b))
 return generation
 @staticmethod
 def mutate(value):
 """Mutate the value by 10 points in either direction."""
 min_, max_ = value - 10, value + 10
 return randint(min_, max_)
def breed_and_select_fittest(individual):
 """Return the fittest member of a generation."""
 generation = Organism.spawn(individual)
 fittest = generation[0]
 for member in generation:
 if member.fitness < fittest.fitness:
 fittest = member
 return fittest
if __name__ == '__main__':
 individual = Organism() # abiogenesis!
 while True:
 individual = breed_and_select_fittest(individual)
 print individual.fitness
 time.sleep(0.2)
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Sep 8, 2016 at 2:18
\$\endgroup\$
1
  • \$\begingroup\$ The only "issue" is that the correct solutions are really easy to find. :) But working on toy problems is a good way to get started! \$\endgroup\$ Commented Sep 8, 2016 at 4:29

2 Answers 2

6
\$\begingroup\$

That is, more or less, a genetic algorithm, though it only implements mutation and not recombination:

@classmethod
def recombine(cls, parentA, parentB):
 traits = {}
 for t in ("r", "g", "b"):
 traits[t] = (getattr(parentA, t) + getattr(parentB, t))/2
 return cls(**traits)

There are, of course, many ways to implement recombination. For example, instad of the simple averaging I do above, you could instead choose a random parent to take each trait from. The best solution depends on your problem space.

To learn more, I'd suggest googling and finding tutorials or examples, and then trying to solve some more realistic problems with them. Can you adapt them to solve any ProjectEuler problems? Or find another application where they're useful?

answered Sep 8, 2016 at 3:58
\$\endgroup\$
1
\$\begingroup\$

Your breed_and_select function can be more succinctly written as:

import operator
def breed_and_select_fittest(individual):
 """Return the fittest member of a generation."""
 generation = Organism.spawn(individual)
 return min(generation, key=operator.attrgetter("fitness"))

It is better to use operator.attrgetter than lambda here, because it is faster (even though performance is not your limiting factor, your execution time is dominated by the time.sleep(0.2) right now).

I would also make this slightly more customizable by adding the size of each generation and the variation during the spawning as parameters.

I'm also not convinced spawn should really be a @classmethod. After all it is the individual spawning the next generation. Consider this:

import operator
class Organism:
 ...
 def spawn(self, n=10, variance=10):
 """Return a mutated generation of `n` members."""
 return [self.__class__(*self.mutate(variance)) for _ in range(n)]
 def mutate(self, variance):
 for value in (self.r, self.g, self.b):
 yield randint(value - variance, value + variance) 
 ...
def breed_and_select_fittest(individual):
 """Return the fittest member of a generation."""
 generation = individual.spawn()
 return min(generation, key=operator.attrgetter("fitness"))

Note that the default start value for range is already 0, so no need for that.

answered Sep 8, 2016 at 6:20
\$\endgroup\$
4
  • \$\begingroup\$ I would use operator.attrgetter('fitness') instead of a lambda for the key. \$\endgroup\$ Commented Sep 8, 2016 at 10:13
  • \$\begingroup\$ @MathiasEttinger Yeah, was thinking about that as well. Is there really such a big difference in speed between the two? Because operator.attrgetter is implemented in C? \$\endgroup\$ Commented Sep 8, 2016 at 10:22
  • \$\begingroup\$ There should be, yes. \$\endgroup\$ Commented Sep 8, 2016 at 10:27
  • \$\begingroup\$ @MathiasEttinger Alright, I'm convinced :) \$\endgroup\$ Commented Sep 8, 2016 at 12:15

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.