HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Change random list of numbers into all 5's using simple evolution simulation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
randomsimpleevolutionsimulationallintonumbersusinglistchange

Problem

This is supposed to be an evolutionary algorithmic program, and I'm not sure if it does what it's supposed to do.

It is supposed to take a random list of numbers (containing at least one 5), and over many generations, increase the probability of 5's reproducing, thus having the 5's eventually taking over the output.

Is this an accurate representation of an evolutionary algorithm? If so, how can I make it better, and if not, how can I make it one?

import random as r
import os
import sys
import time
from subprocess import call
#call('color a', shell=True)
class Entity(object):
    def __init__(self, number):
        self.number = number
        self.chance = 10
        self.age = 0
class Fitness(object):
    def __init__(self, population):
        self.population = population

    def StartPopulation(self, pop, entities):
        for x in range(pop):
            entity = Entity(r.randint(1, 10))
            entities.append(entity)

    def FitnessMethod(self, entities):

        for x in entities:
            if x.number == 5:
                x.chance += 10
            else: 
                x.chance += 0

            if x.chance > 100:
                x.chance = 100

            else: 
                x.chance += 0

    def Reproduce(self,entities):
        x = entities[r.randint(0, (len(entities) - 1))]
        if r.randint(0, 100) = 1:
                entities.remove(x)          

pop = r.randint(5, 10)
entities = []
Fitness = Fitness(pop)
Fitness.StartPopulation(pop, entities)
for x in entities:
    print x.number, x.chance
print "~~~~~~~~~~~~~\n"
raw_input()

for x in range(200):
    Fitness.FitnessMethod(entities)
    Fitness.Reproduce(entities)
    Fitness.Aging(entities)
    for x in entities:
        print x.number, x.age, x.chance
    print "Generation_Mutation_Complete"    
raw_input()

Solution

Picking up where Josay left off:
Simulating evolution

The current algorithm has some flaws as an evolution simulation:

  • Entities inherit their parents' fitness. Fitness should be determined by the entity's traits; it isn't a trait in its own right.



  • Entities only age when they reproduce, and the number of offspring doesn't depend on the fitness. This means all entities reproduce eventually; fitter ones just do it faster! Furthermore, the number of offspring doesn't depend on the fitness, so all entities have the same reproductive success! In an evolution simulation, fitter organisms should have more reproductive success.



Some other oddities:

  • Only one organism per step can reproduce. Why not let them all reproduce at once?



  • Randomness is introduced in too many places: in particular, the fitness is random, but it's also compared to a random number. You only need one of these. It's better to keep randomness out of the fitness function entirely, so its results are more meaningful and it's easier to debug.



  • The population grows without bound. It should probably be limited, perhaps with random.sample.



It's not necessary to model aging at all in a simple evolution simulator. You can simply have every organism die in every generation. Or you can have them die randomly, without keeping track of age.

There are two important parts of a genetic algorithm:

-
The fitness function: given an organism, how successful is it?

-
The breeding function: Given a population and a fitness function, produce the next generation. There are a lot of options here: you can choose parents randomly weighted by fitness, or let everything reproduce a few times and keep the fittest children, or let everything reproduce according to its fitness and keep random children.

Design

Fitness does not make much sense as a class, because its instances don't represent anything. If each instance represents a population, it could be a Population. But it's simpler to represent a population as a list of organisms, with no separate class. (Not everything needs to be a class!)

Some of the methods of Fitness should be operations on a single Entity, not a whole population.

Several functions modify objects unnecessarily. In particular, fitness_method should simply return a fitness rather than modifying the Entity. (This causes a bug: the fitness of 5 increases over time!)
Minor style issues

Some names could be improved:

  • fitness_method is fitness. Method names should say what they do, not the fact that they're methods.



  • aging should be removeOldOrganisms.



  • Entity is vague. Organism is more specific. Populations can be called population, not entities.



The loop to print a population is repeated and should be factored out.

raw_input can print a prompt, so the separate print before it is unnecessary. But there's no need to wait for user input at all.

It's not necessary to rename random.
How I'd do it

Here's a version with most of the above changes:

import random

class Organism:
    def __init__(self, number):
        self.number = number
    def fitness(self):
        "How many children can this organism have?"
        return 3 if self.number == 5 else 2
    def reproduce(self):
        "Make a child. Currently children are just copies of the parent."
        return Organism(self.number)

max_population = 100

def nextGeneration(population):
    children = []
    for x in population:
        n_children = random.randint(0, x.fitness())
        children += [x.reproduce() for _ in range(n_children)]
    return random.sample(children, min(len(children), max_population))

def printPopulation(population):
    for x in population:
        print x.number, x.fitness()

population = [Organism(random.randint(1, 10)) for _ in range(10)]
printPopulation(population)

for gen in range(10):
    print "Generation", gen, "population", len(population)
    population = nextGeneration(population)
    printPopulation(population)


This uses fitness to determine the number of children rather than which children survive; either is reasonable.

Code Snippets

import random

class Organism:
    def __init__(self, number):
        self.number = number
    def fitness(self):
        "How many children can this organism have?"
        return 3 if self.number == 5 else 2
    def reproduce(self):
        "Make a child. Currently children are just copies of the parent."
        return Organism(self.number)

max_population = 100

def nextGeneration(population):
    children = []
    for x in population:
        n_children = random.randint(0, x.fitness())
        children += [x.reproduce() for _ in range(n_children)]
    return random.sample(children, min(len(children), max_population))

def printPopulation(population):
    for x in population:
        print x.number, x.fitness()

population = [Organism(random.randint(1, 10)) for _ in range(10)]
printPopulation(population)

for gen in range(10):
    print "Generation", gen, "population", len(population)
    population = nextGeneration(population)
    printPopulation(population)

Context

StackExchange Code Review Q#49780, answer score: 6

Revisions (0)

No revisions yet.