/* Branch-and-Bound solution to the Traveling Salesman Problem. This is * based on the backtracking solution that generates all Hamiltonian * paths and then simply reports the smallest one. Here the bounding * function is the length of the path that has already been fixed in the * permutation vector. The presently mutable part of the permutation * vector may or may not presently constitute a path --- but some other * permutation of that segment may constitute a path. * * Based on a boolean in the class Tour, one can examine DFS processing: * the primary key is the index (reversed to give maxheap behavior), * with the secondary key as the distance. For one specimen dataset * (fairly sparse), the same number of Tour objects were generated for * the two search options. DFS, however, found a valid tour very early. * * Input file specifications: * Number of vertices in the graph (crossroads in the map) * That many lines giving names * one blank line * Lines specifying roads: two double-quoted names and an integer * * Language: Java 1.5.x due to Scanner, enhanced for loop . . . * * Author: Timothy Rolfe */ import java.util.*; // Arrays and Collections and . . . public class TSP_BnB { static int[][] wt; // Matrix of edge weights static String[] city; // Vector of city names static int n; // Dimension for wt and city static ArrayList<Tour> soln = new ArrayList<Tour>(); static int bestTour; // Initialized in init() static int blocked; // Ditto static boolean DEBUG = true; // Show accept/reject decisions static boolean VERBOSE = true; // Show all tours discovered private static class Tour implements Comparable { int[] soln; int index; // In branch-and-bound, start of variable int dist; static int nTours = 0; // Best-first based on dist, or DFS based on maxheap of index static boolean DFS = true; static boolean DBG = true; /* Presumable edges up to [index-1] have been verified before * this constructor has been called. So compute the fixed * distance from [0] up to [index-1] as dist. */ private Tour(int[] vect, int index, int[][] wt) { dist = 0; for (int k = 1; k < index; k++) // Add edges dist += wt[vect[k-1]][vect[k]]; if ( index == n ) dist += wt[vect[n-1]][vect[0]]; // Return edge soln = new int[n]; // Deep copy System.arraycopy(vect, 0, soln, 0, n); this.index = index; // Index to permute nTours++; // Count up # of tours if ( DBG ) System.out.printf("Idx %d: %s\n", index, toString() ); } public int compareTo ( Object o ) { Tour rt = (Tour)o; int c1 = rt.index - this.index, c2 = this.dist - rt.dist; if ( DFS ) return c1 == 0 ? c2 : c1; else return c2; } // For debugging convenience: show the current state. public String toString() { StringBuilder val = new StringBuilder( city[soln[0]] ); for ( int k = 1; k < n; k++ ) val.append(", " + city[soln[k]]); val.append(", " + city[soln[0]]); val.append( String.format(" for %d", dist) ); return val.toString(); } } // For debugging convenience: show state UP TO this index private static void partial(int[] vect, int index) { int dist = 0; System.out.print (city[vect[0]]); for ( int k = 1; k <= index; k++ ) { System.out.print (", " + city[vect[k]]); dist += wt[vect[k-1]][vect[k]]; } System.out.println (" for distance " + dist); } // Initialize the global variables based on the file passed through // the Scanner inp. See the header for the specifications of the // input file. private static void init(Scanner inp) { int sub1, sub2; String line; n = inp.nextInt(); wt = new int[n][n]; city = new String[n]; // Initially, there are NO edges; hence -1. for ( sub1 = 0; sub1 < n; sub1++ ) Arrays.fill(wt[sub1], -1); inp.nextLine(); // Discard rest of first line for ( sub1 = 0; sub1 < n; sub1++ ) city[sub1] = inp.nextLine(); Arrays.sort(city); // Just to be sure (binarySearch) inp.nextLine(); // Discard blank spacing line; blocked = 0; // Accumulate ALL weights for upper bound while ( inp.hasNext() ) { int head, tail; int dist; String src, dst; line = inp.nextLine(); // E.g.: "George" "Pasco" 91 // Chop out the double-quoted substrings. head = line.indexOf('"') + 1; tail = line.indexOf('"', head); src = line.substring(head, tail); head = line.indexOf('"', tail+1) + 1; tail = line.indexOf('"', head); dst = line.substring(head, tail); dist = Integer.parseInt( line.substring(tail+1).trim() ); sub1 = Arrays.binarySearch(city, src); sub2 = Arrays.binarySearch(city, dst); wt[sub1][sub2] = wt[sub2][sub1] = dist; blocked += dist; } blocked += blocked; // Double the total bestTour = blocked; // And initialize bestTour } // Used below in generating permutations. private static void swap ( int[] x, int p, int q ) { int tmp = x[p]; x[p] = x[q]; x[q] = tmp; } // Generate the available tours by branch-and-bound. // Generate the initial permutation vector, then save that state // as the first examined in the branch-and-bound. public static void tour() { int[] vect = new int[n]; int start; Queue<Tour> work = new PriorityQueue<Tour>(); // First permutation vector. for ( int k = 0; k < n; k++ ) vect[k] = k; // We will, however, start from Spokane, not Coulee City // --- IF the data file is the one for the inland Pacific NW start = Arrays.binarySearch(city, "Spokane"); if ( start >= 0 ) { vect[start] = 0; vect[0] = start; } // Consequently, we start the permutations at [1], NOT [0]. work.add( new Tour(vect, 1, wt) ); while ( ! work.isEmpty() ) // Branch-and-bound loop { Tour current = work.poll(); int index = current.index; vect = current.soln; if ( index == n ) // I.e., Full permutation vector { if ( wt[vect[n-1]][vect[0]] > 0 ) // Return edge? { if ( current.dist < bestTour ) // Better than earlier? {//Save the state in the list bestTour = current.dist; soln.add(current); if ( DEBUG ) System.out.println("Accept " + current); } else if ( DEBUG ) System.out.println ("Too long: " + current); } else if (DEBUG) System.out.println( "Invalid: " + current); } else // Continue generating permutations { int k; // Loop variable int hold; // Used in regenerating the original state for ( k = index; k < n; k++ ) { swap ( vect, index, k ); if ( wt[vect[index-1]][vect[index]] < 0 ) continue; work.add ( new Tour(vect, index+1, wt) ); } // Restore original permutation hold = vect[index]; for ( k = index+1; k < n; k++ ) vect[k-1] = vect[k]; vect[n-1] = hold; } } } public static void main (String[] args) throws Exception { String filename = args.length == 0 ? "RoadSet.txt" : args[0]; Scanner inp = new Scanner ( new java.io.File(filename) ); System.out.println("Data read from file " + filename); init(inp); tour(); if (VERBOSE) { System.out.println ("Tours discovered:"); for ( Tour opt : soln ) System.out.println(opt); } if ( soln.size() == 0 ) { System.out.println("NO tours discovered. Exiting."); System.exit(0); } System.out.println (Tour.nTours + " Tour objects generated."); Collections.sort(soln); System.out.println("Best tour: "); System.out.println(soln.get(0)); } }This is Backtracking:
http://penguin.ewu.edu/cscd501/Wint-2008/BranchAndBound/TSP/index.html/* Backtracking solution to the Traveling Salesman Problem. Generates all * Hamiltonian paths through recursion and keep them in a list. Then * simply report the smallest one. There is no bounding function used. * A Tour object is only generated when a complete tour has been found. * * NOTE: This implementation generates ALL tours. With a small change * it can be revised to discard solutions worse than the current best * solution so far (global bestTour field). * * Input file expectation: * Number of vertices in the graph (crossroads in the map) * That many lines giving names * one blank line * Lines specifying roads: two double-quoted names and an integer * * Language: Java 1.5.x due to Scanner, enhanced for loop . . . * * Author: Timothy Rolfe */ import java.util.*; // Arrays and Collections and . . . public class TSP_Back { static int[][] wt; // Matrix of edge weights static String[] city; // Vector of city names static int n; // Dimension for wt and city static ArrayList<Tour> soln = new ArrayList<Tour>(); static int bestTour = Integer.MAX_VALUE; static boolean DEBUG = true; // Show accept/reject decisions static boolean VERBOSE = true; // Show all tours discovered // Comparable allows sorting the ArrayList of solutions for smallest. private static class Tour implements Comparable { int[] soln; // Permutation vector of city subscripts int dist; private Tour(int[] vect) { dist = wt[vect[n-1]][vect[0]]; // Start with return edge for (int k = 1; k < n; k++) // Add in all the others dist += wt[vect[k-1]][vect[k]]; soln = new int[n]; // Deep copy of the vector System.arraycopy(vect, 0, soln, 0, n); } public int compareTo ( Object o ) { return this.dist - ((Tour)o).dist; } // For debugging convenience: show the current state. public String toString() { StringBuilder val = new StringBuilder(city[soln[0]]); for ( int k = 1; k < n; k++ ) val.append(", " + city[soln[k]]); val.append(", " + city[soln[0]]); val.append( String.format(" for %d", dist) ); return val.toString(); } } // For debugging convenience: show state UP TO this index private static void partial(int[] vect, int index) { int dist = 0; System.out.print (city[vect[0]]); for ( int k = 1; k <= index; k++ ) { System.out.print (", " + city[vect[k]]); dist += wt[vect[k-1]][vect[k]]; } System.out.println (" for distance " + dist); } // Initialize the global variables based on the file passed through // the Scanner inp. See the header documentation for the // specifications for the input file. private static void init(Scanner inp) { int sub1, sub2; String line; n = inp.nextInt(); wt = new int[n][n]; city = new String[n]; // Initially, there are NO edges; hence -1. for ( sub1 = 0; sub1 < n; sub1++ ) Arrays.fill(wt[sub1], -1); inp.nextLine(); // Discard rest of first line for ( sub1 = 0; sub1 < n; sub1++ ) city[sub1] = inp.nextLine(); Arrays.sort(city); // Just to be sure (binarySearch) inp.nextLine(); // Discard blank spacing line; while ( inp.hasNext() ) { int head, tail; int dist; String src, dst; line = inp.nextLine(); // E.g.: "George" "Pasco" 91 // Chop out the double-quoted substrings. head = line.indexOf('"') + 1; tail = line.indexOf('"', head); src = line.substring(head, tail); head = line.indexOf('"', tail+1) + 1; tail = line.indexOf('"', head); dst = line.substring(head, tail); dist = Integer.parseInt( line.substring(tail+1).trim() ); sub1 = Arrays.binarySearch(city, src); sub2 = Arrays.binarySearch(city, dst); wt[sub1][sub2] = wt[sub2][sub1] = dist; } } // Public access for generating the tours. // Generate the initial permutatio vector, then call recursive tour public static void tour() { int[] vect = new int[n]; int start = Arrays.binarySearch(city, "Spokane"); // First permutation vector. for ( int k = 0; k < n; k++ ) vect[k] = k; // We will, however, start from Spokane, not Coulee City // --- IF the data file is the one for the inland Pacific NW start = Arrays.binarySearch(city, "Spokane"); if ( start >= 0 ) { vect[start] = 0; vect[0] = start; } // Consequently, we start the permutations at [1], NOT [0]. tour(1, vect); } // Used below in generating permutations. private static void swap ( int[] x, int p, int q ) { int tmp = x[p]; x[p] = x[q]; x[q] = tmp; } // Recursive generation of the permutatio vectors that constitute // possible paths. private static void tour(int index, int[] vect) { if ( index == n ) // I.e., we have a full permutation vector { Tour current; if ( wt[vect[n-1]][vect[0]] > 0 ) // IS there a return edge? {//Save the state in the list current = new Tour(vect); bestTour = Math.min(current.dist, bestTour); soln.add(current); if ( DEBUG ) System.out.println("Accept " + current); } else if (DEBUG) { System.out.print ("Reject "); partial ( vect, n-1 ); } } else // Continue generating permutations { int k; // Loop variable int hold; // Used in regenerating the original state for ( k = index; k < n; k++ ) { swap ( vect, index, k ); if ( wt[vect[index-1]][vect[index]] < 0 ) continue; tour ( index+1, vect ); } // Restore permutation hold = vect[index]; for ( k = index+1; k < n; k++ ) vect[k-1] = vect[k]; vect[n-1] = hold; } } public static void main (String[] args) throws Exception { String filename = args.length == 0 ? "RoadSet.txt" : args[0]; Scanner inp = new Scanner ( new java.io.File(filename) ); System.out.println("Data read from file " + filename); init(inp); tour(); if (VERBOSE) { System.out.println ("Tours discovered:"); for ( Tour opt : soln ) System.out.println(opt); } if ( soln.size() == 0 ) System.out.println("NO tours discovered. Exiting."); else { Collections.sort(soln); System.out.println("Best tour: "); System.out.println(soln.get(0)); System.out.println("Worst tour: "); // First of the pair with this total. System.out.println(soln.get(soln.size()-2)); } } } /* Run with DEBUG false and VERBOSE true Data read from file Levitin.txt Tours discovered: a, b, c, d, e, a for 24 a, b, c, e, d, a for 19 a, b, d, c, e, a for 24 a, b, d, e, c, a for 16 a, b, e, c, d, a for 23 a, b, e, d, c, a for 20 a, c, b, d, e, a for 25 a, c, b, e, d, a for 24 a, c, d, b, e, a for 29 a, c, d, e, b, a for 20 a, c, e, b, d, a for 24 a, c, e, d, b, a for 16 a, d, b, c, e, a for 28 a, d, b, e, c, a for 24 a, d, c, b, e, a for 32 a, d, c, e, b, a for 23 a, d, e, b, c, a for 24 a, d, e, c, b, a for 19 a, e, b, c, d, a for 32 a, e, b, d, c, a for 29 a, e, c, b, d, a for 28 a, e, c, d, b, a for 24 a, e, d, b, c, a for 25 a, e, d, c, b, a for 24 Best tour: a, b, d, e, c, a for 16 Worst tour: a, d, c, b, e, a for 32 */
Traversal Salesmane Problem - branch-and-bound
最新推荐文章于 2022-07-22 01:01:45 发布