/// <summary> /// performs a Branch and Bound search of the state space of partial tours /// stops when time limit expires and uses BSSF as solution /// </summary> /// <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)</returns> public string[] bBSolveProblem() { // Overall the worst time and space complexity would be O(n!) and // the time complexity would be O(n!) where n is the amount of // cities possible for our route. We know, however, that the branch // and bound method will do much better as it prunes off states from the // queue. If we consider the pruning doing a really good job, // then time complexity tends to be O(n^3) and space complexity tends to be // O(n^3). This is because ideally be prune often enough that the final // solution has the most space. string[] results = new string[3]; Stopwatch timer = new Stopwatch(); int count = 0; timer.Start(); string[] greedy_r = greedySolveProblem(); // Gready takes O(n^2) time and O(n) space. State root = new State(Cities.Length); List <State> queue = new List <State>(); // O(n^2) time and O(n^2) space for n cities in a State S. for (int i = 0; i < Cities.Length; i++) { for (int j = 0; j < Cities.Length; j++) { if (i == j) { root.matrix[i, j] = double.PositiveInfinity; } else { root.matrix[i, j] = Cities[i].costToGetTo(Cities[j]); } } } root.find_lb(); // Takes a most O(n^3) time. root.route.Add(Cities[0]); double root_lb = root.lower_bound; int max_states = 1; int updates = 0; int states_created = 0; int pruned = 0; int from = 0; State at_node = root; do { // The do-while loop would cost at most O(n!) becuase each node would // be considered. But the branch and bound method reduces this significantly. double current_count = bssf.costOfRoute(); foreach (int to in at_node.get_children(from)) { // At most there will be n - 1 children to get. Therefore, // this loop will cost O(n) for each state calculated. if (to == 0 && at_node.route.Count == Cities.Length) { if (root_lb == at_node.lower_bound) { count++; updates++; bssf = new TSPSolution(at_node.route); timer.Stop(); results[COST] = costOfBssf().ToString(); results[TIME] = timer.Elapsed.ToString(); results[COUNT] = count.ToString(); return(results); } TSPSolution t_bssf = new TSPSolution(at_node.route); if (t_bssf.costOfRoute() < current_count) { count++; updates++; bssf = t_bssf; } } else if (to != 0) { State temp = new State(Cities.Length); states_created++; temp.lower_bound = at_node.matrix[from, to]; // Copying takes O(n^2) time. Array.Copy(at_node.matrix, temp.matrix, Cities.Length * Cities.Length); List <Tuple <int, int> > zeros = new List <Tuple <int, int> >(); for (int j = 0; j < Cities.Length; j++) { // This loop checks if we are turing a zero into infinity // and then changes a whole row and column into infinity // (See TSP branch and bound for details). if (temp.matrix[from, j] == 0 && j != to) { zeros.Add(new Tuple <int, int>(from, j)); } if (temp.matrix[j, to] == 0 && j != from) { zeros.Add(new Tuple <int, int>(j, to)); } temp.matrix[from, j] = double.PositiveInfinity; temp.matrix[j, to] = double.PositiveInfinity; } if (temp.matrix[from, to] == 0) { zeros.Add(new Tuple <int, int>(from, to)); } temp.matrix[to, from] = double.PositiveInfinity; // Used to use find_lb() but at wost check_zeros is O(n^2) // instead of O(n^3). temp.check_zeros(zeros); //temp.lb_find() temp.lower_bound += at_node.lower_bound; if (temp.lower_bound < current_count) { temp.number = to; temp.depth = at_node.depth + 1; temp.do_priority(); temp.route = new ArrayList(at_node.route); temp.route.Add(Cities[to]); queue.Add(temp); } else { pruned++; } } } // This sorting time takes O(nlog(n)) time. // The queue space compexity is complicated because we don't // really know how many States will be on the queue. The worst case // is O(n!) but we usually do not get to this value. (See table in report) queue = queue.OrderByDescending(x => x.priority).ToList(); if (max_states < queue.Count) { max_states = queue.Count; } pruned--; // necessary because the do-while structure do { pruned++; at_node = queue[queue.Count - 1]; queue.RemoveAt(queue.Count - 1); // Removing at end of array saves time. from = at_node.number; } while (at_node.lower_bound > current_count && queue.Count > 0); if (timer.Elapsed.TotalSeconds > ProblemAndSolver.TIME_LIMIT) { break; } } while (queue.Count > 0); timer.Stop(); results[COST] = costOfBssf().ToString(); results[TIME] = timer.Elapsed.ToString(); results[COUNT] = count.ToString(); return(results); }