[GRASS-SVN] r51621 - in grass-addons/grass7/vector: . v.net.salesman.opt

svn_grass at osgeo.org svn_grass at osgeo.org
Mon May 14 10:31:55 EDT 2012


Author: mmetz
Date: 2012-05-14 07:31:54 -0700 (Mon, 14 May 2012)
New Revision: 51621

Added:
   grass-addons/grass7/vector/v.net.salesman.opt/
Modified:
   grass-addons/grass7/vector/v.net.salesman.opt/main.c
Log:
v.net.salesman optimized version

Modified: grass-addons/grass7/vector/v.net.salesman.opt/main.c
===================================================================
--- grass/trunk/vector/v.net.salesman/main.c	2012-05-12 11:58:29 UTC (rev 51616)
+++ grass-addons/grass7/vector/v.net.salesman.opt/main.c	2012-05-14 14:31:54 UTC (rev 51621)
@@ -5,7 +5,7 @@
  *  
  *  AUTHOR(S):    Radim Blazek, Markus Metz
  *                
- *  PURPOSE:      Create a cycle connecting given nodes.
+ *  PURPOSE:      Create a tour connecting given nodes.
  *                
  *  COPYRIGHT:    (C) 2001-2011 by the GRASS Development Team
  * 
@@ -22,21 +22,17 @@
 #include <grass/vector.h>
 #include <grass/dbmi.h>
 #include <grass/glocale.h>
+#include "local_proto.h"
 
 /* TODO: Use some better algorithm */
 
-typedef struct
-{
-    int city;
-    double cost;
-} COST;
-
 int ncities;			/* number of cities */
 int nnodes;			/* number of nodes */
 int *cities;			/* array of cities */
 int *cused;			/* city is in cycle */
 COST **costs;			/* pointer to array of pointers to arrays of sorted forward costs */
 COST **bcosts;			/* pointer to array of pointers to arrays of sorted backward costs */
+double **cost_cache;		/* pointer to array of pointers to arrays of cached costs */
 int *cycle;			/* path */
 int ncyc = 0;			/* number of cities in cycle */
 int debug_level;
@@ -62,8 +58,8 @@
 
 	cycle[after + 1] = city;
     }
+    ncyc++;
     cused[city] = 1;
-    ncyc++;
 
     if (debug_level >= 2) {
 	G_debug(2, "Cycle:");
@@ -94,16 +90,15 @@
     return 0;
 }
 
-
 int main(int argc, char **argv)
 {
     int i, j, k, ret, city, city1;
     int nlines, type, ltype, afield, tfield, geo, cat;
     int node, node1, node2, line;
-    double **cost_cache;			/* pointer to array of pointers to arrays of cached costs */
+    int last_opt = 0, ostep, optimize;
     struct Option *map, *output, *afield_opt, *tfield_opt, *afcol, *abcol,
 	*seq, *type_opt, *term_opt;
-    struct Flag *geo_f;
+    struct Flag *geo_f, *opt_f;
     struct GModule *module;
     struct Map_info Map, Out;
     struct ilist *TList;	/* list of terminal nodes */
@@ -127,10 +122,10 @@
     G_add_keyword(_("network"));
     G_add_keyword(_("salesman"));
     module->label =
-	_("Creates a cycle connecting given nodes (Traveling salesman problem).");
+	_("Creates a tour connecting given nodes (Traveling salesman problem).");
     module->description =
 	_("Note that TSP is NP-hard, heuristic algorithm is used by "
-	  "this module and created cycle may be sub optimal");
+	  "this module and created tour may be suboptimal");
 
     map = G_define_standard_option(G_OPT_V_INPUT);
     output = G_define_standard_option(G_OPT_V_OUTPUT);
@@ -179,6 +174,10 @@
     geo_f->description =
 	_("Use geodesic calculation for longitude-latitude locations");
 
+    opt_f = G_define_flag();
+    opt_f->key = 'o';
+    opt_f->description = _("Optimize the tour");
+
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
 
@@ -211,10 +210,8 @@
 	}
     }
 
-    if (geo_f->answer)
-	geo = 1;
-    else
-	geo = 0;
+    geo = geo_f->answer;
+    optimize = opt_f->answer;
 
     Vect_check_input_output_name(map->answer, output->answer, G_FATAL_EXIT);
 
@@ -284,6 +281,7 @@
     /* Create sorted lists of costs */
     /* for a large number of cities this will become very slow, can not be fixed */
     G_message(_("Creating cost cache..."));
+    G_begin_distance_calculations();
     for (i = 0; i < ncities; i++) {
 	G_percent(i, ncities, 2);
 	k = 0;
@@ -339,7 +337,7 @@
 	}
     }
 
-    G_message(_("Searching for the shortest cycle..."));
+    G_message(_("Searching for the shortest tour..."));
     /* find 2 cities with largest distance */
     cost = city = -1;
     for (i = 0; i < ncities; i++) {
@@ -359,12 +357,14 @@
     /* In each step, find not used city, with biggest cost to any used city, and insert 
      *  into cycle between 2 nearest nodes */
     /* for a large number of cities this will become very slow, can be fixed */
-    for (i = 0; i < ncities - 2; i++) {
-	G_percent(i, ncities - 3, 1);
+
+    /* for (i = 0; i < ncities - 2; i++) { */
+    while (ncyc < ncities) {
+	G_percent(ncyc, ncities, 1);
 	cost = -1;
 	G_debug(2, "---- city %d ----", i);
 	for (j = 0; j < ncities; j++) {
-	    if (cused[j] == 1)
+	    if (cused[j])
 		continue;
 	    tmpcost = 0;
 	    for (k = 0; k < ncities - 1; k++) {
@@ -420,8 +420,6 @@
 	    
 	    /* tmpcost must always be > 0 */
 
-	    G_debug(2, "? %d - %d cost = %f x %f", node1, node2, tmpcost,
-		    cost);
 	    /* always true for j = 0 */
 	    if (tmpcost < cost) {
 		city1 = j;
@@ -429,13 +427,101 @@
 	    }
 	}
 	add_city(city, city1);
+
+	/* keep ostep sufficiently large */
+	ostep = sqrt(ncyc) * 2 / 3;
+
+	if (ostep < 2)
+	    ostep = 2;
+	if (ostep > MAX_CHAIN_LENGTH && ncities < 3000) /* max 3000 */
+	    ostep = MAX_CHAIN_LENGTH;
+
+	if (optimize && ncyc > 5) {
+	    int chain_length;
+	    int success;
+	    
+	    success = optimize_nbrs(city1);
+	    if (success)
+		G_debug(3, "Neighbours optimized? %s",
+		        success ? "Yes" : "No");
+
+	    if (last_opt < ncyc - ostep) {
+		
+		/* use brute force optimization
+		 * don't overdo it here, it's costly in terms of time
+		 * and the benefits are small */
+
+		chain_length = ostep;
+		if (chain_length > ncyc / 2)
+		    chain_length = ncyc / 2;
+		if (chain_length > MAX_CHAIN_LENGTH)
+		    chain_length = MAX_CHAIN_LENGTH;
+		if (chain_length == 0)
+		    chain_length = 1;
+
+		success = optimize_tour_chains(chain_length, chain_length,
+		                               ostep, 0, 0);
+		if (success)
+		    G_debug(3, "Preliminary tour optimized? %s",
+		            success ? "Yes" : "No");
+
+		last_opt = ncyc;
+	    }
+	    
+	}  /* optimize done */
     }
     
-    /* TODO: optimize tour (some Lin–Kernighan method) */
+    /* TODO: optimize tour using some refined Lin–Kernighan method */
+    if (optimize && ncyc > 3) {
+	double ocost, ocost1;
+	int max_chain_length, chain_length;
+	int success = 0;
+	int optiter = 1;
 
+	/* brute force bootstrapping */
+	ocost = 0.;
+	cycle[ncyc] = cycle[0];
+	for (j = 0; j < ncyc; j++) {
+	    ocost += cost_cache[cycle[j]][cycle[j + 1]];
+	}
+	G_verbose_message(_("Current total cost: %.3f"), ocost);
+
+	max_chain_length = MAX_CHAIN_LENGTH < ncyc / 2 ? MAX_CHAIN_LENGTH : ncyc / 2;
+
+	G_message(_("Optimizing..."));
+
+	for (chain_length = max_chain_length; chain_length > 0; chain_length--) {
+	    success = 0;
+
+	    G_message("%d. iteration...", optiter++);
+
+	    success = optimize_tour_chains(max_chain_length, 1, 1, 1, 1);
+	    
+	    if (!success)
+		break;
+	}
+	success = 1;
+	while (success) {
+	    success = 0;
+	    G_message("%d. iteration...", optiter++);
+	    for (i = 0; i < ncyc; i++) {
+		if (optimize_nbrs(i))
+		    success = 1;
+	    }
+	}
+
+	ocost1 = 0.;
+	cycle[ncyc] = cycle[0];
+	for (j = 0; j < ncyc; j++) {
+	    ocost1 += cost_cache[cycle[j]][cycle[j + 1]];
+	}
+	G_verbose_message(_("Optimized total cost: %.3f, gain %.3f%%"),
+	                  ocost1, ocost / ocost1 * 100 - 100);
+    }  /* optimize done */
+
     if (debug_level >= 2) {
 	/* debug print */
-	G_debug(2, "Cycle:");
+	G_debug(2, "Tour:");
 	for (i = 0; i < ncities; i++) {
 	    G_debug(2, "%d: %d: %d", i, cycle[i], cities[cycle[i]]);
 	}
@@ -465,7 +551,7 @@
     Vect_open_new(&Out, output->answer, Vect_is_3d(&Map));
     Vect_hist_command(&Out);
 
-    G_verbose_message(_("Cycle with total cost %.3f"), cost);
+    G_verbose_message(_("Tour with total cost %.3f"), cost);
     G_debug(2, "Arcs' categories (layer %d, %d arcs):", afield,
 	    StArcs->n_values);
 



More information about the grass-commit mailing list