-
Notifications
You must be signed in to change notification settings - Fork 24
/
algorithms.cpp
355 lines (299 loc) · 9.28 KB
/
algorithms.cpp
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// algorithms.cpp - Implementation of tsp class, which manages the data
// for my traveling salesman problem algorithms. Cities for the problem
// are stored as two deques of pointers to city objects, original_list
// contains the initial list of cities read from file and solution contains
// the order of the current solution to the problem.
// Written by Sam Best for CS325
// Last modified 3/12/2013
#include "algorithms.h"
#include <iostream>
#include <deque>
#include <fstream>
#include <cstdlib>
using namespace std;
int done = 0;
// Constructor that takes a character string corresponding to an external filename and reads in cities from that file
tsp::tsp(const char * filename)
{
num_cities = read_file(filename);
}
// Copy constructor
tsp::tsp(tsp & source)
{
int size = source.original_list.size();
for (int i = 0; i < size; ++i)
{
original_list.push_back(new city(*source.original_list[i]));
}
size = source.solution.size();
for (int i = 0; i < size; ++i)
{
solution.push_back(new city(*source.solution[i]));
}
num_cities = source.num_cities;
}
// Destructor clears the deques
tsp::~tsp()
{
original_list.clear();
solution.clear();
}
// Read city data from file into tsp's solution member, returns number of cities added
int tsp::read_file(const char * filename)
{
int added = 0;
int id_read = 0;
int x_read = 0;
int y_read = 0;
original_list.clear();
ifstream read(filename); // open file
if (!read)
{
cout << "error";
return 0; // file doesn't exist
}
while (read>>id_read>>x_read>>y_read) // go till end of file
{
original_list.push_back(new city(id_read, x_read, y_read, id_read, false));
++added;
}
read.close();
return added;
}
// Public wrapper, calls recursive brute force function
int tsp::brute_force_wrapper()
{
int distance = 0;
int min_distance = 9999999;
deque <city*> best_path;
if (solution.empty())
nearest_neighbor_basic(0);
brute_force(best_path, min_distance, num_cities);
copy_city_deque(best_path, solution); // copy best solution into solution
distance = get_solution_distance();
write_solution("bruteforce.txt");
return distance;
}
// Recursive brute force algorithm to calculate all permutations of tours and find the optimal one.
void tsp::brute_force(deque <city*> & best_path, int & min_distance, int cities_left)
{
int current_dist = 0;
signal(SIGTERM, end_opt);
for (int i = 0; !done && i < cities_left; ++i)
{
rotate(cities_left-1);
current_dist = get_solution_distance();
if (current_dist < min_distance)
{
min_distance = current_dist;
cout << min_distance << endl;
copy_city_deque(solution, best_path);
}
brute_force(best_path, min_distance, cities_left - 1); // shift ending position of rotation left 1 in each recursion
}
return;
}
// Called by brute force. Moves solution[pos] to start of the solution deque
void tsp::rotate(int pos)
{
solution.push_front(solution[pos]);
solution.erase(solution.begin() + pos+1);
return;
}
// Finds an initial tour using nearest neighbor, optimizes with basic 2-opt algorithm
int tsp::nearest_neighbor()
{
solution.clear();
int total_dist = 0;
int best_start_distance = 9999999;
int last_run = 0;
// Run through entire 2-opt optimization for each city, write best index.
for (int i = 0; !done && i < num_cities; ++i)
{
nearest_neighbor_basic(i);
last_run = two_change();
if (last_run < best_start_distance)
{
best_start_distance = last_run;
cout << "Writing solution " << best_start_distance << endl;
write_solution(OUTPUT_FN); // write each time an improvement is found
}
}
total_dist = get_solution_distance();
if (best_start_distance <= total_dist)
return best_start_distance; // solution already written
else
{
cout << "Writing solution " << total_dist << endl;
write_solution(OUTPUT_FN); //write current solution (midway through 2-opt)
return best_start_distance;
}
}
// Generates nearest neighbor tour in solution from list of cities in original_list, starting at start_index.
int tsp::nearest_neighbor_basic(int start_index)
{
int cities_added = 0;
int closest = 9999999;
int total_dist = 0;
int current_dist = 0;
int closest_index = 0;
int current_num = num_cities;
deque <city*> temp;
copy_city_deque(original_list, temp); // save original list
solution.clear();
solution.push_back(original_list[start_index]); // move first city to solution
original_list.erase(original_list.begin() + start_index); // erase from original_list
--current_num; // cities remaining in original_list
++cities_added; // number of cities in solution so far
while(current_num != 0) // loop until no cities remaining in original_list
{
closest = 9999999; // reset closest to a large number so that comparison will work
for (int i = 0; i < current_num; ++i)
{
current_dist = original_list[i]->dist(solution[cities_added-1]);
if (current_dist < closest)
{
closest_index = i;
closest = current_dist;
}
}
total_dist += closest;
solution.push_back(original_list[closest_index]);
original_list.erase(original_list.begin() + closest_index);
--current_num;
++cities_added;
}
copy_city_deque(temp, original_list); // restore original list
return total_dist + solution[0]->dist(solution[cities_added-1]);
}
// 2-opt neighbor solution check
int tsp::two_change()
{
deque <city*> new_path;
int min_distance = get_solution_distance();
bool start_over = false;
signal(SIGTERM, end_opt); //signal handler, ends optimization if it receives SIGTERM
while(!done)
{
start_over = false;
for (int i = 1; i < num_cities && !start_over; ++i)
{
for (int j = i+1; j < num_cities-1 && !start_over; ++j)
{
// only check moves that will reduce distance
if (solution[i-1]->dist(solution[j]) + solution[i]->dist(solution[j+1]) < solution[i-1]->dist(solution[i]) + solution[j]->dist(solution[j+1]))
{
swap_two(i, j);
min_distance = get_solution_distance();
start_over = true;
}
else
start_over = false;
}
}
if (!start_over)
break;
}
return min_distance;
}
// 2-opt with neighbor list (NOT WORKING)
int tsp::two_opt()
{
deque <city*> new_path;
int min_distance = get_solution_distance();
int k = 0;
for (int i = 1; i < num_cities ; ++i)
{
k = 1;
fix_positions();
while (k <= 5 && solution[i]->dist(solution[i]->get_neighbor(k)) < solution[i-1]->dist(solution[i]))
{
swap_two(i, solution[i]->get_neighbor_pos(k));
min_distance = get_solution_distance();
++k;
cout << min_distance << endl;
}
fix_positions();
}
return min_distance;
}
// Reverses the order of the cities from i to k.
int tsp::swap_two(int i, int k)
{
deque <city*> temp;
int count = 0;
// Reverse order
for (int x = k; x >= i; --x)
{
temp.push_back(solution[x]);
}
for (int x = i; x <= k; ++x)
{
solution[x] = temp[count];
++count;
}
temp.clear();
//fix_positions(); // used for neighbor list algorithm
return 1;
}
// Calculate the total distance of the tour in solution
int tsp::get_solution_distance()
{
int total_dist = 0;
for (int i = 0; i < num_cities - 1; ++i)
{
total_dist += solution[i]->dist(solution[i+1]);
}
total_dist += solution[0]->dist(solution[num_cities-1]);
return total_dist;
}
// Write solution to file_name
void tsp::write_solution(const char * file_name)
{
int distance = get_solution_distance();
ofstream write(file_name);
if (write.is_open())
{
write << distance << '\n';
}
for (int i = 0; i < num_cities; ++i)
{
solution[i]->write_out(write);
}
write.close();
return;
}
// Displays all the neighbor lists (used for testing)
void tsp::display_neighbor_lists()
{
for (int i = 0; i < num_cities; ++i)
{
cout << "LIST " << i << endl;
solution[i]->display_neighbor_list();
}
return;
}
// Updates the stored positions of the cities to their current value
void tsp::fix_positions()
{
for (int i = 0; i < num_cities; ++i)
{
solution[i]->set_pos(i);
}
}
// Copies a deque of city pointers from source to dest
void copy_city_deque(deque <city*> & source, deque <city*> & dest)
{
int length = source.size();
dest.clear();
for (int i = 0; i < length; ++i)
{
dest.push_back(new city(*source[i]));
}
}
// Catches the sigterm signal and updates global variable done to end optimization loops
void end_opt(int signum)
{
cout << "\nOut of time\n";
done = 1;
}