//creates a new node given its parent and the index of which city it should follow next public bBNode(bBNode parent, int chosen) { //keep a reference to the list of cities this.cities = parent.cities; //make deep copies of important data structures this.route = new ArrayList(parent.route.ToArray()); this.lookup = new ArrayList(parent.lookup.ToArray()); this.used = new bool[cities.Length]; parent.used.CopyTo(this.used, 0); this.matrix = parent.matrix.Clone() as double[, ]; this.bound = 0; bound += parent.bound; //add the next chosen city to the route route.Add(cities[chosen]); //mark the city as used used[chosen] = true; //add the index of the city to the lookup array lookup.Add(chosen); //handle & reduce matrix if (route.Count > 1) { int prev = (int)lookup[lookup.Count - 2]; this.bound += matrix[prev, chosen]; matrix[prev, chosen] = double.PositiveInfinity; matrix[chosen, prev] = double.PositiveInfinity; infiniteRow(prev); infiniteCol(chosen); } Reduce(); }
/** * Returns the best node in the queue. * Replaces the top node with the last node, bubbles down, and decreases size of queue. */ public bBNode DeleteBestRoute() { bBNode item = queue[1]; BubbleDown(queue[count], 1); queue[count] = null; count--; return(item); }
/** * Lets a node move up the queue while it has a higher priority than its parent */ private void BubbleUp(bBNode item, int index) { while (index != 1 && queue[index / 2] < item) { queue[index] = queue[index / 2]; queue[index / 2].SetIndex(index); index = index / 2; } queue[index] = item; item.SetIndex(index); }
/** * Inserts a node into the array at the bottom and lets it bubble up. */ public void Insert(bBNode item) { item.SetIndex(states); states++; count++; if (count > maxcount) { maxcount = count; } BubbleUp(item, count); }
/** * returns a list of children nodes * each child node adds another unvisited city to its route */ public List <bBNode> Expand() { List <bBNode> children = new List <bBNode>(); for (int c = 0; c < cities.Length; c++) { if (used[c]) { continue; } bBNode child = new bBNode(this, c); children.Add(child); } return(children); }
/** * Lets a node move down the queue while it has a lower priority than its children */ private void BubbleDown(bBNode item, int index) { int child_index = MaxChild(index); bBNode child_item = queue[child_index]; while (child_index != 0 && child_item > item) { queue[index] = child_item; child_item.SetIndex(index); index = child_index; child_index = MaxChild(index); child_item = queue[child_index]; } queue[index] = item; item.SetIndex(index); }
/** * Given an index into the queue, this returns the child with the higher priority */ private int MaxChild(int index) { if (2 * index > count) { return(0); } else { bBNode rh_child = queue[2 * index]; bBNode lh_child = queue[2 * index + 1]; if (rh_child > lh_child) { return(2 * index); } else { return(2 * index + 1); } } }
/// <summary> /// performs a Branch and Bound search of the state space of partial tours /// stops when time limit expires and uses BSSF as solution /// @returns results array for GUI that contains three ints: cost of solution, time spent to find solution, number of solutions found during search (not counting initial BSSF estimate) public string[] bBSolveProblem() { // counts the number of bssf updates / solutions found int count = 0; // counts the number of pruned states int pruned = 0; // counts the number of total states int total_states = 0; //container for results string[] results = new string[3]; //priority queue implementation HeapQueue myqueue = new HeapQueue(); Route = new ArrayList(); Stopwatch timer = new Stopwatch(); timer.Start(); Route.Clear(); //generate greedy bssf as initial guess greedySolveProblem(); //initialize a matrix with all the cities bBNode init = new bBNode(GetCities()); /** * Starts at the first city (0). * It doesn't matter where we start because we are looking for a "loop" of cities. * And we don't want to repeatedly find the same solution by starting at a different city. */ bBNode root = new bBNode(init, 0); bBNode next; //initialize queue myqueue.MakeQueue((int)Math.Pow(Cities.Length, 2), root); total_states++; //while the queue is not empty and we haven't hit the time limit . . . while (!myqueue.IsEmpty() && timer.ElapsedMilliseconds < this.time_limit) { // take the top node in the queue next = (bBNode)myqueue.DeleteBestRoute(); //if it has a better solution than the bssf, go deeper, otherwise, we prune it. if (next.getBound() < bssf.costOfRoute()) { // check if the route is finished if (next.getRouteLength() == Cities.Length) { //We found a better solution! bssf = new TSPSolution(next.getRoute()); count++; } else { // expand the node, and only insert the children that do better than the bssf List <bBNode> children = next.Expand(); foreach (bBNode c in children) { total_states++; if (c.getBound() < bssf.costOfRoute()) { myqueue.Insert(c); } else { pruned++; } } } } else { pruned++; } } timer.Stop(); Console.WriteLine("**************************************"); Console.WriteLine("# cities: " + Cities.Length); Console.WriteLine("# seed: " + _seed); Console.WriteLine("RunningTime: " + timer.Elapsed); Console.WriteLine("Best tour: " + costOfBssf()); Console.WriteLine("Max stored states: " + myqueue.getMaxStoredStates()); Console.WriteLine("Number of Solutions: " + count); Console.WriteLine("Number total states: " + total_states); Console.WriteLine("Total pruned states: " + pruned); results[COST] = costOfBssf().ToString(); // load results array results[TIME] = timer.Elapsed.ToString(); results[COUNT] = count.ToString(); return(results); }
/** * Initializes the queue with the size and the first node. */ public void MakeQueue(int size, bBNode start) { queue = new bBNode[size + 1]; Insert(start); }