forked from lukedodd/ant-tsp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AntTsp.java
311 lines (273 loc) · 9.42 KB
/
AntTsp.java
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
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Random;
/*
* === Implementation of ant swarm TSP solver. ===
*
* The algorithm is described in [1, page 8].
*
* == Tweaks/notes ==
* - I added a system where the ant chooses with probability
* "pr" to go to a purely random town. This did not yield better
* results so I left "pr" fairly low.
* - Used an approximate pow function - the speedup is
* more than a factor of 10! And accuracy is not needed
* See AntTsp.pow for details.
*
* == Parameters ==
* I set the parameters to values suggested in [1]. My own experimentation
* showed that they are pretty good.
*
* == Usage ==
* - Compile: javac AntTsp.java
* - Run: java AntTsp <TSP file>
*
* == TSP file format ==
* Full adjacency matrix. Columns separated by spaces, rows by newline.
* Weights parsed as doubles, must be >= 0.
*
* == References ==
* [1] M. Dorigo, The Ant System: Optimization by a colony of cooperating agents
* ftp://iridia.ulb.ac.be/pub/mdorigo/journals/IJ.10-SMC96.pdf
*
*/
public class AntTsp {
// Algorithm parameters:
// original amount of trail
private double c = 1.0;
// trail preference
private double alpha = 1;
// greedy preference
private double beta = 5;
// trail evaporation coefficient
private double evaporation = 0.5;
// new trail deposit coefficient;
private double Q = 500;
// number of ants used = numAntFactor*numTowns
private double numAntFactor = 0.8;
// probability of pure random selection of the next town
private double pr = 0.01;
// Reasonable number of iterations
// - results typically settle down by 500
private int maxIterations = 2000;
public int n = 0; // # towns
public int m = 0; // # ants
private double graph[][] = null;
private double trails[][] = null;
private Ant ants[] = null;
private Random rand = new Random();
private double probs[] = null;
private int currentIndex = 0;
public int[] bestTour;
public double bestTourLength;
// Ant class. Maintains tour and tabu information.
private class Ant {
public int tour[] = new int[graph.length];
// Maintain visited list for towns, much faster
// than checking if in tour so far.
public boolean visited[] = new boolean[graph.length];
public void visitTown(int town) {
tour[currentIndex + 1] = town;
visited[town] = true;
}
public boolean visited(int i) {
return visited[i];
}
public double tourLength() {
double length = graph[tour[n - 1]][tour[0]];
for (int i = 0; i < n - 1; i++) {
length += graph[tour[i]][tour[i + 1]];
}
return length;
}
public void clear() {
for (int i = 0; i < n; i++)
visited[i] = false;
}
}
// Read in graph from a file.
// Allocates all memory.
// Adds 1 to edge lengths to ensure no zero length edges.
public void readGraph(String path) throws IOException {
FileReader fr = new FileReader(path);
BufferedReader buf = new BufferedReader(fr);
String line;
int i = 0;
while ((line = buf.readLine()) != null) {
String splitA[] = line.split(" ");
LinkedList<String> split = new LinkedList<String>();
for (String s : splitA)
if (!s.isEmpty())
split.add(s);
if (graph == null)
graph = new double[split.size()][split.size()];
int j = 0;
for (String s : split)
if (!s.isEmpty())
graph[i][j++] = Double.parseDouble(s) + 1;
i++;
}
n = graph.length;
m = (int) (n * numAntFactor);
// all memory allocations done here
trails = new double[n][n];
probs = new double[n];
ants = new Ant[m];
for (int j = 0; j < m; j++)
ants[j] = new Ant();
}
// Approximate power function, Math.pow is quite slow and we don't need accuracy.
// See:
// http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
// Important facts:
// - >25 times faster
// - Extreme cases can lead to error of 25% - but usually less.
// - Does not harm results -- not surprising for a stochastic algorithm.
public static double pow(final double a, final double b) {
final int x = (int) (Double.doubleToLongBits(a) >> 32);
final int y = (int) (b * (x - 1072632447) + 1072632447);
return Double.longBitsToDouble(((long) y) << 32);
}
// Store in probs array the probability of moving to each town
// [1] describes how these are calculated.
// In short: ants like to follow stronger and shorter trails more.
private void probTo(Ant ant) {
int i = ant.tour[currentIndex];
double denom = 0.0;
for (int l = 0; l < n; l++)
if (!ant.visited(l))
denom += pow(trails[i][l], alpha)
* pow(1.0 / graph[i][l], beta);
for (int j = 0; j < n; j++) {
if (ant.visited(j)) {
probs[j] = 0.0;
} else {
double numerator = pow(trails[i][j], alpha)
* pow(1.0 / graph[i][j], beta);
probs[j] = numerator / denom;
}
}
}
// Given an ant select the next town based on the probabilities
// we assign to each town. With pr probability chooses
// totally randomly (taking into account tabu list).
private int selectNextTown(Ant ant) {
// sometimes just randomly select
if (rand.nextDouble() < pr) {
int t = rand.nextInt(n - currentIndex); // random town
int j = -1;
for (int i = 0; i < n; i++) {
if (!ant.visited(i))
j++;
if (j == t)
return i;
}
}
// calculate probabilities for each town (stored in probs)
probTo(ant);
// randomly select according to probs
double r = rand.nextDouble();
double tot = 0;
for (int i = 0; i < n; i++) {
tot += probs[i];
if (tot >= r)
return i;
}
throw new RuntimeException("Not supposed to get here.");
}
// Update trails based on ants tours
private void updateTrails() {
// evaporation
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
trails[i][j] *= evaporation;
// each ants contribution
for (Ant a : ants) {
double contribution = Q / a.tourLength();
for (int i = 0; i < n - 1; i++) {
trails[a.tour[i]][a.tour[i + 1]] += contribution;
}
trails[a.tour[n - 1]][a.tour[0]] += contribution;
}
}
// Choose the next town for all ants
private void moveAnts() {
// each ant follows trails...
while (currentIndex < n - 1) {
for (Ant a : ants)
a.visitTown(selectNextTown(a));
currentIndex++;
}
}
// m ants with random start city
private void setupAnts() {
currentIndex = -1;
for (int i = 0; i < m; i++) {
ants[i].clear(); // faster than fresh allocations.
ants[i].visitTown(rand.nextInt(n));
}
currentIndex++;
}
private void updateBest() {
if (bestTour == null) {
bestTour = ants[0].tour;
bestTourLength = ants[0].tourLength();
}
for (Ant a : ants) {
if (a.tourLength() < bestTourLength) {
bestTourLength = a.tourLength();
bestTour = a.tour.clone();
}
}
}
public static String tourToString(int tour[]) {
String t = new String();
for (int i : tour)
t = t + " " + i;
return t;
}
public int[] solve() {
// clear trails
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
trails[i][j] = c;
int iteration = 0;
// run for maxIterations
// preserve best tour
while (iteration < maxIterations) {
setupAnts();
moveAnts();
updateTrails();
updateBest();
iteration++;
}
// Subtract n because we added one to edges on load
System.out.println("Best tour length: " + (bestTourLength - n));
System.out.println("Best tour:" + tourToString(bestTour));
return bestTour.clone();
}
// Load graph file given on args[0].
// (Full adjacency matrix. Columns separated by spaces, rows by newlines.)
// Solve the TSP repeatedly for maxIterations
// printing best tour so far each time.
public static void main(String[] args) {
// Load in TSP data file.
if (args.length < 1) {
System.err.println("Please specify a TSP data file.");
return;
}
AntTsp anttsp = new AntTsp();
try {
anttsp.readGraph(args[0]);
} catch (IOException e) {
System.err.println("Error reading graph.");
return;
}
// Repeatedly solve - will keep the best tour found.
for (; ; ) {
anttsp.solve();
}
}
}