|
| 1 | +""" |
| 2 | +# Example usage |
| 3 | +from genetic import * |
| 4 | +target = 371 |
| 5 | +p_count = 100 |
| 6 | +i_length = 6 |
| 7 | +i_min = 0 |
| 8 | +i_max = 100 |
| 9 | +p = population(p_count, i_length, i_min, i_max) |
| 10 | +fitness_history = [grade(p, target),] |
| 11 | +for i in xrange(100): |
| 12 | + p = evolve(p, target) |
| 13 | + fitness_history.append(grade(p, target)) |
| 14 | +
|
| 15 | +for datum in fitness_history: |
| 16 | + print datum |
| 17 | +""" |
| 18 | +from random import randint, random |
| 19 | +from operator import add |
| 20 | + |
| 21 | +def individual(length, min, max): |
| 22 | + 'Create a member of the population.' |
| 23 | + return [ randint(min,max) for x in xrange(length) ] |
| 24 | + |
| 25 | +def population(count, length, min, max): |
| 26 | + """ |
| 27 | + Create a number of individuals (i.e. a population). |
| 28 | +
|
| 29 | + count: the number of individuals in the population |
| 30 | + length: the number of values per individual |
| 31 | + min: the minimum possible value in an individual's list of values |
| 32 | + max: the maximum possible value in an individual's list of values |
| 33 | +
|
| 34 | + """ |
| 35 | + return [ individual(length, min, max) for x in xrange(count) ] |
| 36 | + |
| 37 | +def fitness(individual, target): |
| 38 | + """ |
| 39 | + Determine the fitness of an individual. Higher is better. |
| 40 | +
|
| 41 | + individual: the individual to evaluate |
| 42 | + target: the target number individuals are aiming for |
| 43 | + """ |
| 44 | + sum = reduce(add, individual, 0) |
| 45 | + return abs(target-sum) |
| 46 | + |
| 47 | +def grade(pop, target): |
| 48 | + 'Find average fitness for a population.' |
| 49 | + summed = reduce(add, (fitness(x, target) for x in pop)) |
| 50 | + return summed / (len(pop) * 1.0) |
| 51 | + |
| 52 | +def evolve(pop, target, retain=0.2, random_select=0.05, mutate=0.01): |
| 53 | + graded = [ (fitness(x, target), x) for x in pop] |
| 54 | + graded = [ x[1] for x in sorted(graded)] |
| 55 | + retain_length = int(len(graded)*retain) |
| 56 | + parents = graded[:retain_length] |
| 57 | + # randomly add other individuals to |
| 58 | + # promote genetic diversity |
| 59 | + for individual in graded[retain_length:]: |
| 60 | + if random_select > random(): |
| 61 | + parents.append(individual) |
| 62 | + # mutate some individuals |
| 63 | + for individual in parents: |
| 64 | + if mutate > random(): |
| 65 | + pos_to_mutate = randint(0, len(individual)-1) |
| 66 | + # this mutation is not ideal, because it |
| 67 | + # restricts the range of possible values, |
| 68 | + # but the function is unaware of the min/max |
| 69 | + # values used to create the individuals, |
| 70 | + individual[pos_to_mutate] = randint( |
| 71 | + min(individual), max(individual)) |
| 72 | + # crossover parents to create children |
| 73 | + parents_length = len(parents) |
| 74 | + desired_length = len(pop) - parents_length |
| 75 | + children = [] |
| 76 | + while len(children) < desired_length: |
| 77 | + male = randint(0, parents_length-1) |
| 78 | + female = randint(0, parents_length-1) |
| 79 | + if male != female: |
| 80 | + male = parents[male] |
| 81 | + female = parents[female] |
| 82 | + half = len(male) / 2 |
| 83 | + child = male[:half] + female[half:] |
| 84 | + children.append(child) |
| 85 | + parents.extend(children) |
| 86 | + return parents |
0 commit comments