2 A D implementation of the "tsp" Olden benchmark, the traveling
\r
5 R. Karp, "Probabilistic analysis of partitioning algorithms for the
\r
6 traveling-salesman problem in the plane." Mathematics of Operations Research
\r
7 2(3):209-224, August 1977.
\r
9 Converted to D and optimized by leonardo maffi, V.1.0, Oct 29 2009.
\r
11 Removed output unless an option is passed by Leandro Lucarella, 2010-08-04.
\r
12 Downloaded from http://www.fantascienza.net/leonardo/js/
\r
13 http://www.fantascienza.net/leonardo/js/dolden_tsp.zip
\r
17 import tango.stdc.stdio: printf, fprintf, stderr;
\r
18 import tango.stdc.time: CLOCKS_PER_SEC, clock;
\r
19 import tango.math.Math: sqrt, log;
\r
21 import Integer = tango.text.convert.Integer;
\r
22 alias Integer.parse toInt;
\r
24 import std.c.stdio: printf, fprintf, stderr;
\r
25 import std.c.time: CLOCKS_PER_SEC, clock;
\r
26 import std.math: sqrt, log;
\r
28 version (D_Version2) {
\r
29 import std.conv: to;
\r
30 alias to!(int, char[]) toInt;
\r
32 import std.conv: toInt;
\r
38 return clock() / cast(double)CLOCKS_PER_SEC;
\r
43 Basic uniform random generator: Minimal Standard in Park and
\r
44 Miller (1988): "Random Number Generators: Good Ones Are Hard to
\r
45 Find", Comm. of the ACM, 31, 1192-1201.
\r
46 Parameters: m = 2^31-1, a=48271.
\r
48 Very simple portable random number generator adapted
\r
49 from Pascal code by Jesper Lund:
\r
50 http://www.gnu-pascal.de/crystal/gpc/en/mail1390.html
\r
52 final class Random {
\r
53 const int m = int.max;
\r
54 const int a = 48271;
\r
55 const int q = m / a;
\r
56 const int r = m % a;
\r
60 this(int the_seed) {
\r
61 this.seed = the_seed;
\r
64 public double nextDouble() {
\r
66 seed = a * (seed - k * q) - r * k;
\r
69 return cast(double)seed / m;
\r
72 public int nextInt(int max) {
\r
74 int k = cast(int)(n * this.nextDouble());
\r
75 return (k == n) ? k - 1 : k;
\r
82 * A class that represents a node in a binary tree. Each node represents
\r
83 * a city in the TSP benchmark.
\r
87 * The number of nodes (cities) in this subtree
\r
92 * The coordinates that this node represents
\r
94 private double x, y;
\r
97 * Left and right child of tree
\r
99 private Tree left, right;
\r
102 * The next pointer in a linked list of nodes in the subtree. The list
\r
103 * contains the order of the cities to visit.
\r
108 * The previous pointer in a linked list of nodes in the subtree. The list
\r
109 * contains the order of the cities to visit.
\r
115 // used by the random number generator
\r
116 private const double M_E2 = 7.3890560989306502274;
\r
117 private const double M_E3 = 20.08553692318766774179;
\r
118 private const double M_E6 = 403.42879349273512264299;
\r
119 private const double M_E12 = 162754.79141900392083592475;
\r
121 public static void initRnd(int seed) {
\r
122 rnd = new Random(seed);
\r
126 * Construct a Tree node (a city) with the specified size
\r
127 * @param size the number of nodes in the (sub)tree
\r
128 * @param x the x coordinate of the city
\r
129 * @param y the y coordinate of the city
\r
130 * @param left the left subtree
\r
131 * @param right the right subtree
\r
133 this(int size, double x, double y, Tree l, Tree r) {
\r
144 * Find Euclidean distance between this node and the specified node.
\r
145 * @param b the specified node
\r
146 * @return the Euclidean distance between two tree nodes.
\r
148 double distance(Tree b) {
\r
149 return sqrt((x - b.x) * (x - b.x) + (y - b.y) * (y - b.y));
\r
153 * Create a list of tree nodes. Requires root to be the tail of the list.
\r
154 * Only fills in next field, not prev.
\r
155 * @return the linked list of nodes
\r
158 Tree myleft, myright, tleft, tright;
\r
159 Tree retval = this;
\r
161 // head of left list
\r
163 myleft = left.makeList();
\r
167 // head of right list
\r
168 if (right !is null)
\r
169 myright = right.makeList();
\r
173 if (myright !is null) {
\r
178 if (myleft !is null) {
\r
180 if (myright !is null)
\r
181 left.next = myright;
\r
191 * Reverse the linked list. Assumes that there is a dummy "prev"
\r
192 * node at the beginning.
\r
195 Tree prev = this.prev;
\r
201 // reverse the list for the other nodes
\r
203 for (Tree t = this.next; t !is null; back = t, t = next) {
\r
209 // reverse the list for this node
\r
215 * Use closest-point heuristic from Cormen, Leiserson, and Rivest.
\r
219 // create the list of nodes
\r
220 Tree t = makeList();
\r
222 // Create initial cycle
\r
225 cycle.next = cycle;
\r
226 cycle.prev = cycle;
\r
228 // Loop over remaining points
\r
230 for ( ; t !is null; t = donext) {
\r
231 donext = t.next; /* value won't be around later */
\r
233 double mindist = t.distance(cycle);
\r
234 for (Tree tmp = cycle.next; tmp != cycle; tmp = tmp.next) {
\r
235 double test = tmp.distance(t);
\r
236 if (test < mindist) {
\r
242 Tree next = min.next;
\r
243 Tree prev = min.prev;
\r
245 double mintonext = min.distance(next);
\r
246 double mintoprev = min.distance(prev);
\r
247 double ttonext = t.distance(next);
\r
248 double ttoprev = t.distance(prev);
\r
250 if ((ttoprev - mintoprev) < (ttonext - mintonext)) {
\r
251 // insert between min and prev
\r
268 * Merge two cycles as per Karp.
\r
269 * @param a a subtree with one cycle
\r
270 * @param b a subtree with the other cycle
\r
272 Tree merge(Tree a, Tree b) {
\r
273 // Compute location for first cycle
\r
275 double mindist = distance(a);
\r
277 for (a = a.next; a != tmp; a = a.next) {
\r
278 double test = distance(a);
\r
279 if (test < mindist) {
\r
285 Tree next = min.next;
\r
286 Tree prev = min.prev;
\r
287 double mintonext = min.distance(next);
\r
288 double mintoprev = min.distance(prev);
\r
289 double ttonext = distance(next);
\r
290 double ttoprev = distance(prev);
\r
293 double tton1, ttop1;
\r
294 if ((ttoprev - mintoprev) < (ttonext - mintonext)) {
\r
295 // would insert between min and prev
\r
301 // would insert between min and next
\r
308 // Compute location for second cycle
\r
310 mindist = distance(b);
\r
312 for (b = b.next; b != tmp; b = b.next) {
\r
313 double test = distance(b);
\r
314 if (test < mindist) {
\r
322 mintonext = min.distance(next);
\r
323 mintoprev = min.distance(prev);
\r
324 ttonext = this.distance(next);
\r
325 ttoprev = this.distance(prev);
\r
328 double tton2, ttop2;
\r
329 if ((ttoprev - mintoprev) < (ttonext - mintonext)) {
\r
330 // would insert between min and prev
\r
336 // would insert between min andn ext
\r
343 // Now we have 4 choices to complete:
\r
344 // 1:t,p1 t,p2 n1,n2
\r
345 // 2:t,p1 t,n2 n1,p2
\r
346 // 3:t,n1 t,p2 p1,n2
\r
347 // 4:t,n1 t,n2 p1,p2
\r
348 double n1ton2 = n1.distance(n2);
\r
349 double n1top2 = n1.distance(p2);
\r
350 double p1ton2 = p1.distance(n2);
\r
351 double p1top2 = p1.distance(p2);
\r
353 mindist = ttop1 + ttop2 + n1ton2;
\r
356 double test = ttop1 + tton2 + n1top2;
\r
357 if (test < mindist) {
\r
362 test = tton1 + ttop2 + p1ton2;
\r
363 if (test < mindist) {
\r
368 test = tton1 + tton2 + p1top2;
\r
369 if (test < mindist)
\r
374 // 1:p1,this this,p2 n2,n1 -- reverse 2!
\r
385 // 2:p1,this this,n2 p2,n1 -- OK
\r
395 // 3:p2,this this,n1 p1,n2 -- OK
\r
404 default: // case 4:
\r
405 // 4:n1,this this,n2 p2,p1 -- reverse 1!
\r
420 * Compute TSP for the tree t. Use conquer for problems <= sz
\r
421 * @param sz the cutoff point for using conquer vs. merge
\r
426 Tree leftval = left.tsp(sz);
\r
427 Tree rightval = right.tsp(sz);
\r
428 return merge(leftval, rightval);
\r
432 * Print the list of cities to visit from the current node. The
\r
433 * list is the result of computing the TSP problem.
\r
434 * The list for the root node (city) should contain every other node
\r
437 void printVisitOrder() {
\r
438 printf("x = %.15f y = %.15f\n", x, y);
\r
439 for (Tree tmp = next; tmp != this; tmp = tmp.next)
\r
440 printf("x = %.15f y = %.15f\n", tmp.x, tmp.y);
\r
444 * Computes the total length of the current tour.
\r
446 double tourLength() {
\r
447 double total = 0.0;
\r
448 Tree precedent = next;
\r
449 Tree current = next.next;
\r
450 if (current == this)
\r
454 total += current.distance(precedent);
\r
455 precedent = current;
\r
456 current = current.next;
\r
457 } while (precedent != this);
\r
459 total += current.distance(this);
\r
464 // static methods ===============================================
\r
467 * Return an estimate of median of n values distributed in [min, max)
\r
468 * @param min the minimum value
\r
469 * @param max the maximum value
\r
471 * @return an estimate of median of n values distributed in [min, max)
\r
473 private static double median(double min, double max, int n) {
\r
474 // get random value in [0.0, 1.0)
\r
475 double t = rnd.nextDouble();
\r
479 retval = log(1.0 - (2.0 * (M_E12 - 1) * (t - 0.5) / M_E12)) / 12.0;
\r
481 retval = -log(1.0 - (2.0 * (M_E12 - 1) * t / M_E12)) / 12.0;
\r
483 // We now have something distributed on (-1.0, 1.0)
\r
484 retval = (retval + 1.0) * (max - min) / 2.0;
\r
485 return retval + min;
\r
489 * Get double uniformly distributed over [min,max)
\r
490 * @return double uniformily distributed over [min,max)
\r
492 private static double uniform(double min, double max) {
\r
493 // get random value between [0.0, 1.0)
\r
494 double retval = rnd.nextDouble();
\r
495 retval = retval * (max - min);
\r
496 return retval + min;
\r
500 * Builds a 2D tree of n nodes in specified range with dir as primary
\r
501 * axis (false for x, true for y)
\r
503 * @param n the size of the subtree to create
\r
504 * @param dir the primary axis
\r
505 * @param min_x the minimum x coordinate
\r
506 * @param max_x the maximum x coordinate
\r
507 * @param min_y the minimum y coordinate
\r
508 * @param max_y the maximum y coordinate
\r
509 * @return a reference to the root of the subtree
\r
511 public static Tree buildTree(int n, bool dir, double min_x,
\r
512 double max_x, double min_y, double max_y) {
\r
520 double med = median(min_x, max_x, n);
\r
521 left = buildTree(n/2, dir, min_x, med, min_y, max_y);
\r
522 right = buildTree(n/2, dir, med, max_x, min_y, max_y);
\r
524 y = uniform(min_y, max_y);
\r
527 double med = median(min_y,max_y,n);
\r
528 left = buildTree(n/2, dir, min_x, max_x, min_y, med);
\r
529 right = buildTree(n/2, dir, min_x, max_x, med, max_y);
\r
531 x = uniform(min_x, max_x);
\r
534 return new Tree(n, x, y, left, right);
\r
541 * Number of cities in the problem.
\r
543 private static int cities;
\r
546 * Set to true if the result should be printed
\r
548 private static bool printResult = false;
\r
551 * Set to true to print informative messages
\r
553 private static bool printMsgs = false;
\r
556 * The main routine which creates a tree and traverses it.
\r
557 * @param args the arguments to the program
\r
559 public static void main(char[] args[]) {
\r
560 if (!parseCmdLine(args))
\r
564 printf("Building tree of size: %d\n", nextPow2(cities+1) - 1);
\r
568 auto t0 = myclock();
\r
569 Tree t = Tree.buildTree(cities, false, 0.0, 1.0, 0.0, 1.0);
\r
571 auto t1 = myclock();
\r
573 auto t2 = myclock();
\r
576 printf("Total tour length: %.15f\n", t.tourLength());
\r
577 auto t3 = myclock();
\r
580 // if the user specifies, print the final result
\r
581 t.printVisitOrder();
\r
584 printf("Tsp build time: %.2f\n", t1 - t0);
\r
585 printf("Tsp computing time: %.2f\n", t2 - t1);
\r
586 printf("Tsp total time: %.2f\n", t3 - t0);
\r
590 private static /*unsigned*/ int nextPow2(/*unsigned*/ int x) {
\r
592 throw new Exception("x must be >= 0");
\r
603 * Parse the command line options.
\r
604 * @param args the command line options.
\r
606 private static final bool parseCmdLine(char[] args[]) {
\r
609 while (i < args.length && args[i][0] == '-') {
\r
610 char[] arg = args[i++];
\r
613 if (i < args.length)
\r
614 cities = toInt(args[i++]);
\r
616 throw new Exception("-c requires the size of tree");
\r
618 throw new Exception("Number of cities must be > 0");
\r
619 } else if (arg == "-p") {
\r
620 printResult = true;
\r
621 } else if (arg == "-m") {
\r
623 } else if (arg == "-h") {
\r
635 * The usage routine which describes the program options.
\r
637 private static final bool usage() {
\r
638 fprintf(stderr, "usage: tsp_d -c <num> [-p] [-m] [-h]\n");
\r
639 fprintf(stderr, " -c number of cities (rounds up to the next power of 2 minus 1)\n");
\r
640 fprintf(stderr, " -p (print the final result)\n");
\r
641 fprintf(stderr, " -m (print informative messages)\n");
\r
642 fprintf(stderr, " -h (print this message)\n");
\r
648 void main(char[][] args) {
\r