-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathGeneticAlgorithm.py
More file actions
97 lines (73 loc) · 3.49 KB
/
GeneticAlgorithm.py
File metadata and controls
97 lines (73 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import random
class SolutionCandidate:
def crossover(self, other):
"""Swaps or shares random parts of the solution 'other' to
create two new solutions. """
raise NotImplementedError("This class has not implemented crossover()")
def mutate(self):
"""Tweaks a random bit of this solution."""
raise NotImplementedError("This class has not implemented mutate()")
def getFitness(self):
"""Returns a non-negative number that determines how well this
solution scores relative to other instances of the same
implementation. The higher the score the more likely this
solution is selected for the next generation. """
raise NotImplementedError("This class has not implemented getFitness()")
def copy(self):
"""Returns a copy of this solution that can be modified
independantly."""
raise NotImplementedError("This class has not implemented copy()")
class SolutionCandidateFactory:
"""GeneticAlgorithm uses this factory to generate (possibly
random) solutions for its first generation."""
def generate():
"""Returns an instance of a class that implements SolutionCandidate."""
pass
class GeneticAlgorithm:
def __init__(self, solutionGenerator, crossoverChance, mutationChance, population):
self.crossoverChance = crossoverChance
self.mutationChance = mutationChance
self.population = population
self.generation = self.createInitialPopulation(solutionGenerator)
def createInitialPopulation(self, solutionGenerator):
solutions = [solutionGenerator.generate() \
for i in range(self.population)]
return solutions
def getSolutions(self):
return self.generation
def getBestSolution(self):
"""Returns the solution that returns the highest fitness score."""
fitnesses = [solution.getFitness() for solution in self.generation]
return self.generation[fitnesses.index(max(fitnesses))]
def select (self, fitnesses):
"""Select a value from the array fitnesses on a random number
based on the fitness values."""
selection = random.random() * sum(fitnesses)
index = 0;
while selection > 0:
selection -= fitnesses[index]
index += 1
return self.generation[index - 1]
def evolve(self):
"""Selects solutions from the current generation using
select() and uses them to create a new generation. Some of the
solutions have a crossover or mutation performed on them."""
# Fitness calculation
fitnesses = [solution.getFitness() for solution in self.generation]
floor = min(fitnesses)
fitnesses = [fitness - floor for fitness in fitnesses]
newGeneration = []
while len(newGeneration) < len(self.generation):
# Select two parents.
parents = [self.select(fitnesses).copy(),
self.select(fitnesses).copy()]
# Randomly run them through crossover()
if random.random() <= self.crossoverChance:
parents[0].crossover(parents[1])
# Randomly run them through mutate()
for parent in parents:
if random.random() <= self.mutationChance:
parent.mutate()
# Add them to the new batch of routes.
newGeneration.extend(parents)
self.generation = newGeneration