1. 1 // Obaid ur Rehman Khattak
2 // In this code, I am implementing the Simulated Annealing heuristic to solve the well
known
3 // Travelling salesman problem. The basic premise of the problem is to decide on the
sequence
4 // of cities to travel starting from an initial city and ending journey at the same
city while
5 // visiting each intermediate city exactly once.
6 // The goal is to minimize the routing distance travelled. This is a well known NP-hard
problem
7 // for which Simulated Annealing is often used.
8
9 // Please note that the code is for teaching purposes only showing the useage of STL
containers
10 // as well as the use of inheritence, polymorphism and use of smart pointers to make
the code
11 // more robust. Various Software engineering practices such as seperating the
definition and
12 // and implementation into seperate .h and .cpp files has been omitted intentionally.
13 // This code was created using Visual Studio 2015 that uses C++14 standard.
14 // Simulated_Annealing.cpp : Defines the entry point for the console application.
15 //
16
17 #include "stdafx.h"
18 #include <iostream>
19 #include <String>
20 #include <vector>
21 #include <utility>
22 #include <cmath>
23 #include <chrono>
24 #include <random>
25 #include <memory>
26
27 using namespace std;
28
29 /*
30 This is the city class which has a name with a possible, an x-coordinate and a
y-coordinate.
31 The city can have alphanumeric characters and spaces in between e.g. "Los Angeles"
32 */
33
34 class City
35 {
36 protected:
37 string name;
38 double x_coord;
39 double y_coord;
40 private:
41 /*
42 This is a private method to print out the city to an output (console, file etc.) It is
used in the
43 overloaded extraction operator.
44 */
45 virtual ostream& write(ostream& os) const
46 {
47 return os << "Name:" << name << "tX-coordinate:" << x_coord <<
"tY-coordinate:" << y_coord << endl;
48 }
49 public:
50 // Constructor
51 City(string lname = "", double lx = 0,double ly = 0):name(lname),
52 x_coord(lx),
53 y_coord(ly)
54 {}
55 // Destructor
56 virtual ~City()
57 {}
58 // Copy Constructor
59 City(const City& src)
3. 128 {
129 y_coord = ly;
130 }
131
132 // Overloaded extraction operator
133 friend ostream& operator<<(ostream& ostr, const City& city)
134 {
135 return city.write(ostr);
136 }
137
138 };
139
140
141 // Metro is a derived class of the Citty class that has an extra string parameter
specifying any
142 // special aspect of the city in question. It has a single extra parameter for the
aspect and
143 // corresponding getter and setter functions as well as overloaded extraction operator.
144
145 class Metro :public City
146 {
147 protected:
148 string remarks;
149 private:
150 virtual ostream& write(ostream& os) const override
151 {
152 return os << "Name:" << name << "tX-coordinate:" << x_coord <<
"tY-coordinate:" << y_coord << "tRemarks:" << remarks << endl;
153 }
154 public:
155 Metro(string name = "", double lx = 0, double ly = 0, string lremarks = "") :
156 City(name, lx, ly),
157 remarks(lremarks)
158 {}
159 virtual ~Metro()
160 {}
161
162 Metro(const Metro& src)
163 {
164 name = src.name;
165 x_coord = src.x_coord;
166 y_coord = src.y_coord;
167 remarks = src.remarks;
168 }
169 Metro& operator= (const Metro& src)
170 {
171 if (this == &src)
172 return *this;
173
174 name = src.name;
175 x_coord = src.x_coord;
176 y_coord = src.y_coord;
177 remarks = src.remarks;
178
179 return *this;
180 }
181
182 Metro(Metro&& src) noexcept
183 {
184 name = src.name;
185 x_coord = src.x_coord;
186 y_coord = src.y_coord;
187 remarks = src.remarks;
188
189 src.name = "";
190 src.x_coord = 0;
191 src.y_coord = 0;
192 src.remarks = "";
193 }
4. 194 Metro& operator=(Metro&& src) noexcept
195 {
196 if (this == &src)
197 return *this;
198 name = src.name;
199 x_coord = src.x_coord;
200 y_coord = src.y_coord;
201 remarks = src.remarks;
202
203 src.name = "";
204 src.x_coord = 0;
205 src.y_coord = 0;
206 src.remarks = "";
207
208 return *this;
209
210 }
211
212 virtual string GetRemarks() const
213 {
214 return remarks;
215 }
216
217 virtual void SetRemarks(const string& lremarks)
218 {
219 remarks = lremarks;
220 }
221
222
223 friend ostream& operator<<(ostream& ostr, const Metro& met)
224 {
225 return met.write(ostr);
226 }
227 };
228
229
230 // We use Euclidean distance to measure the distance between two cities.
231 double GetDistance(const City& lhs, const City& rhs)
232 {
233 double x_diff_square = pow(lhs.GetXCoordinate()- rhs.GetXCoordinate(), 2);
234 double y_diff_square = pow(lhs.GetYCoordinate() - rhs.GetYCoordinate(), 2);
235
236 return sqrt(x_diff_square + y_diff_square);
237 }
238
239
240 // This function actually measures the total routing distance of visiting each of the
cities in question
241 // and returing back to the starting city such that every intermediate city is visited
exactly once.
242
243 double GetEnergy(const vector<shared_ptr<City>>& cities)
244 {
245 double energy = 0;
246 size_t n1 = cities.size() - 1;
247
248 for (size_t index = 0; index < n1;++index)
249 energy += GetDistance(*cities[index], *cities[index + 1]);
250
251 energy += GetDistance(*cities[0], *cities[n1]);
252
253 return energy;
254 }
255
256 // This function is used by the Simulated Annealing algorithm to traverse the solution
space and move between
257 // different points as a function of the routing distances with different permuations
of visting schedules.
258
5. 259 double Acceptance_Probability(double old_energy,
260 double new_energy,
261 double temperature)
262 {
263 double delta_energy = new_energy - old_energy;
264 if (delta_energy <= 0)
265 return 1;
266 else return exp(-1 * delta_energy / temperature);
267
268 }
269
270
271 // Simulated annealing algorithm
272
273 void SimulatedAnnealing(vector<shared_ptr<City>>& cities, // Vector of cities
274 double temperature = 10000, // Starting "temperature"
with default 10000
275 double cooling_rate = 0.004) // Cooling rate.
Determines how fast the
276 // the algorithm
converges. Small rates
277 // correspong to slow
times but increased
278 // coverage of solution
space explored
279 {
280 // We define the seed, the random number generators and the distributions for
randomly
281 // picking the cities to swap in the visiting schedule at each iteration as well as
282 // generating the probability to move between points in the solution space
283 random_device rd;
284 mt19937 engine(rd());
285
286 uniform_int_distribution<size_t> distribution_int_1(0, cities.size()-1);
287 uniform_real_distribution<double> distribution_real(0.0, 1.0);
288
289 vector<shared_ptr<City>> oldState = cities;
290 double oldEnergy = GetEnergy(cities);
291
292 vector<shared_ptr<City>> bestState = oldState;
293 double bestEnergy = oldEnergy;
294
295 vector<shared_ptr<City>> newState = oldState;
296 double newEnergy = oldEnergy;
297
298 size_t index1 = 0, index2 = 0;
299 double accepted = 0;
300
301 while (temperature > 1)
302 {
303
304 vector<shared_ptr<City>> temp = oldState;
305
306 index1 = distribution_int_1(engine);
307
308 do
309 {
310 index2 = distribution_int_1(engine);
311 } while (index2 == index1);
312
313 swap(temp[index1], temp[index2]);
314
315
316 newState = temp;
317 newEnergy = GetEnergy(temp);
318
319 accepted = Acceptance_Probability(oldEnergy, newEnergy, temperature);
320
321 if (distribution_real(engine) < accepted)
6. 322 {
323 oldState = newState;
324 oldEnergy = newEnergy;
325
326 if (oldEnergy < bestEnergy)
327 {
328 bestState = oldState;
329 bestEnergy = oldEnergy;
330 }
331 }
332
333
334 temperature = temperature*(1 - cooling_rate);
335
336 }
337
338 cities.swap(bestState);
339
340 }
341
342
343 // This function is used to input data from the user as to how many cities are to be
visited and
344 // the various properties of the cities such as the coordinates, the name of the city
and whether
345 // it is a metro or not.
346
347 // Note the use of smart pointers in the inputs. They serve two purposes. One is to be
able to move
348 // data objects that might be very "heavy" in terms of the memory they use and the
other important
349 // reason is to be able to include both base and derived classes City and Metro using
polymorphism
350 // and avoid object slicing which is loss of information incurred when an object of
type city
351 // is assigned an object of a derived class which would throw away the fields that are
not in the base
352 // class
353
354 void InputValues(vector<shared_ptr<City>>& cities, int numCities)
355 {
356 int index = 0;
357 string name,remarks,answer;
358 double lx = 0;
359 double ly = 0;
360
361 while (index < numCities)
362 {
363 cout << "Enter name of city" << (index + 1) << ":";
364 getline(cin, name);
365
366 // The following loops is used for input validation to make sure all the data
fed into the
367 // simulated annealing algorithm has valid inputs.
368
369 while (true)
370 {
371 string dummy;
372 size_t sz = 0;
373 bool done = true;
374 cout << "Enter x coordinate:";
375 try
376 {
377 getline(cin, dummy);
378 lx = stod(dummy,&sz);
379 }
380 catch (const exception& e)
381 {
382 cerr << "Error:" << e.what() << "Please try again.n";