-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeneticAlgorithm.cpp
More file actions
160 lines (149 loc) · 5.66 KB
/
GeneticAlgorithm.cpp
File metadata and controls
160 lines (149 loc) · 5.66 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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//
// Created by danny on 2018-11-09.
//
#include "GeneticAlgorithm.hpp"
/**
* Evolves a population by moving the fittest tour to the front, adding
* child tours to the rest of the population, and then mutating them
* @param p population being genetically evolved
* @return
*/
void GeneticAlgorithm::evolve(Population & p) {
Population newPop;
Tour fit = p.getFittestTour();
vector<Tour> newPopTours;
newPopTours.push_back(fit);
vector<Tour> parents = selectParents(p);
for (int j = NUMBER_OF_ELITES; j < Population::getSize(); ++j) {
Tour child = crossover(parents.at(0), parents.at(1));
newPopTours.push_back(child);
}
for (int i = NUMBER_OF_ELITES; i < Population::getSize(); i++) {
mutate(newPopTours.at((unsigned long) i));
}
newPop.setTours(newPopTours);
p = newPop;
}
/**
* Finds the fittest tour in a population and moves it to the front
* of the tour list
* @param p population to find fittest tour in
*/
void GeneticAlgorithm::selection(Population& p) const {
Tour fittest = p.getFittestTour();
for (auto it = p.getTours().begin(); it != p.getTours().end(); ++it) {
if (*it == fittest) {
std::rotate(p.getTours().begin(), it, p.getTours().end());
break;
}
}
}
/**
* Takes two parent tours and creates a child tour by filling it with
* a random number of cities from parent 1, and filling the rest of the
* tour with cities from parent 2
* @param t1 parent tour 1
* @param t2 parent tour 2
* @return child tour
*/
Tour GeneticAlgorithm::crossover(Tour t1, Tour t2) {
Tour child(true);
vector<City> parent1 = t1.getTour();
vector<City> parent2 = t2.getTour();
// Get random cutoff number
auto endPos = RandomNumGenerator::getInstance().
getIntegerInRange(0, (int) parent1.size() - 1);
// Loop and add the sub tour from parent1 to our child
for (int i = 0; i <= endPos; ++i) {
child.getTour().push_back(t1.getCity(i));
}
int newCount = 0;
// Loop through parent2's tour and add city if it doesn't already exist
while (child.getTour().size() < 32) {
if (!child.containsCity(t2.getCity(newCount))) {
child.getTour().push_back(t2.getCity(newCount));
}
newCount++;
}
return child;
}
/**
* Takes a tour and "mutates" it by randomly selecting two cities at
* a time and swaps their positions in the tour
* @param t tour being mutated
*/
void GeneticAlgorithm::mutate(Tour &t) {
for (unsigned long tourPosition1 = 0; tourPosition1 < t.getTour().size();
++tourPosition1){
auto randDbl = RandomNumGenerator::getInstance()
.getRealInRange(0, 1);
if (randDbl < MUTATION_RATE) {
auto tourPosition2 = RandomNumGenerator::getInstance()
.getIntegerInRange(0, CITIES_IN_TOUR - 1);
// swap
swap(t.getTour().at(tourPosition1),
t.getTour().at((unsigned long) tourPosition2));
}
}
}
/**
* Creates subsets of tours from the population and returns the fittest of
* each subset
* @param p Population for selection
* @return vector of fittest tours from the population subset
*/
vector<Tour> GeneticAlgorithm::selectParents(Population &p) {
selection(p);
vector<Tour> popCopy = p.getTours();
popCopy.erase(popCopy.begin());
popCopy.shrink_to_fit();
vector<Tour> parents;
for (int i = 0; i < NUMBER_OF_PARENTS; ++i) {
vector<Tour> pool;
for (int j = 0; j < PARENT_POOL_SIZE; ++j) {
long seed = chrono::system_clock::now().time_since_epoch().count();
shuffle(popCopy.begin(), popCopy.end(), default_random_engine(seed));
auto randomNum = RandomNumGenerator::getInstance()
.getIntegerInRange(0, (int) p.getTours().size() - 1);
pool.push_back(p.getTour(randomNum));
popCopy.erase(popCopy.begin());
popCopy.shrink_to_fit();
}
sort(pool.begin(), pool.end());
parents.push_back(pool.at(0));
pool.clear();
}
return parents;
}
/**
* Evolves a population using the genetic algorithm for a minimum number of iterations
* specified by ITERATIONS static variable, and stops after it surpasses that number when
* the improvement factor is less than a specified number
* @param p population being evolved by algorithm
*/
void GeneticAlgorithm::optimize(Population &p) {
cout << "Population's starting statistics : \nFitness level: " <<
p.getFittestTour().determineFitness() << "\nFittest tour's total distance: " <<
p.getFittestTour().getTourDistance() << endl;
cout << "============================================" << endl;
cout << "Evolving... please wait..." << endl;
int baseDistance = p.getBaseDistance();
double improvementFactor{0.0};
int cycles{0};
while (cycles < ITERATIONS || improvementFactor > IMPROVEMENT) {
evolve(p);
improvementFactor = (double) p.getFittestTour().getTourDistance()
/ (double) baseDistance;
if (cycles % 100 == 0 && cycles != 0) {
cout << "Fitness level after " << cycles << " evolutions: "
<< p.getFittestTour().determineFitness() << "\nImprovement factor: "
<< setprecision(4) << improvementFactor << "\n..." << endl;
}
cycles++;
}
cout << "============================================" << endl;
cout << "Population's statistics after " << ITERATIONS << " evolutions: "
<< "\nFitness level: " << p.getFittestTour().determineFitness() <<
"\nFittest tour's total distance: " <<
p.getFittestTour().getTourDistance() << endl;
}