[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