/** * This method returns the TSPState of the element with the minimum value and removes it from the queue. * Time Complexity: O(log(|V|)) because removing a node is constant time as we have its position in * the queue, then to read just the heap we just bubble up the min value which takes as long as * the depth of the tree which is log(|V|), where |V| is the number of nodes * Space Complexity: O(1) because we don't create any extra variables that vary with the size of the input. */ public TSPState deleteMin() { // grab the node with min value which will be at the root TSPState minValue = states[1]; states[1] = states[count]; count--; // fix the heap int indexIterator = 1; while (indexIterator <= count) { // grab left child int smallerElementIndex = 2 * indexIterator; // if child does not exist, break if (smallerElementIndex > count) { break; } // if right child exists and is of smaller value, pick it if (smallerElementIndex + 1 <= count && states[smallerElementIndex + 1].getPriority() < states[smallerElementIndex].getPriority()) { smallerElementIndex++; } if (states[indexIterator].getPriority() > states[smallerElementIndex].getPriority()) { // set the node's value to that of its smaller child TSPState temp = states[smallerElementIndex]; states[smallerElementIndex] = states[indexIterator]; states[indexIterator] = temp; } indexIterator = smallerElementIndex; } // return the min value return(minValue); }
/** * This method updates the nodes in the queue after inserting a new node * Time Complexity: O(log(|V|)) as reording the heap works by bubbling up the min value to the top * which takes as long as the depth of the tree which is log|V|. * Space Complexity: O(1) as it does not create any extra variables that vary with the size of the input. */ public void insert(TSPState newState) { // update the count count++; states[count] = newState; if (count > maxNum) { maxNum = count; } // as long as its parent has a larger value and have not hit the root int indexIterator = count; while (indexIterator > 1 && states[indexIterator / 2].getPriority() > states[indexIterator].getPriority()) { // swap the two nodes TSPState temp = states[indexIterator / 2]; states[indexIterator / 2] = states[indexIterator]; states[indexIterator] = temp; indexIterator /= 2; } }
//**********************************************BBAlgorithm********************************************************************************* /// <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() { string[] results = new string[3]; // TODO: Add your implementation for a branch and bound solver here. //Initalize variables. Takes O(1) space and time int numOfCitiesLeft = Cities.Length; int numOfSolutions = 0; int numOfStatesCreated = 0; int numOfStatesNotExpanded = 0; //Initalize time variable for stopping the algorithm after the default of 60 seconds. Takes O(1) space and time DateTime start = DateTime.Now; DateTime end = start.AddSeconds(time_limit / 1000); //Create initial root state and set its priority to its lower bound. Takes O(n^2) space and time as discussed above TSPState initialState = createInitialState(); numOfStatesCreated++; initialState.setPriority(calculateKey(numOfCitiesLeft - 1, initialState.getLowerBound())); //Create initial BSSF greedily double bssfBound = createGreedyBssf(); PriorityQueue queue = new PriorityQueue(Cities.Length); queue.insert(initialState); // Branch and Bound until the queue is empty, we have exceeded the time limit, or we found the optimal solution /* This loop will have a iterate 2^n times approximately with expanding and pruning for each state, then for each state it * does O(n^2) work by reducing the matrix, so over all O((n^2)*(2^n)) time and space as well as it creates a nxn * matrix for each state*/ while (!queue.isEmpty() && DateTime.Now < end && queue.getMinLB() != bssfBound) { // Grab the next state in the queue TSPState currState = queue.deleteMin(); // check if lower bound is less than the BSSF, else prune it if (currState.getLowerBound() < bssfBound) { // Branch and create the child states for (int i = 0; i < Cities.Length; i++) { // First check that we haven't exceeded the time limit if (DateTime.Now >= end) { break; } // Make sure we are only checking cities that we haven't checked already if (currState.getPath().Contains(Cities[i])) { continue; } // Create the State double[,] oldCostMatrix = currState.getCostMatrix(); double[,] newCostMatrix = new double[Cities.Length, Cities.Length]; // Copy the old array in the new one to modify the new without affecting the old for (int k = 0; k < Cities.Length; k++) { for (int l = 0; l < Cities.Length; l++) { newCostMatrix[k, l] = oldCostMatrix[k, l]; } } City lastCityinCurrState = (City)currState.getPath()[currState.getPath().Count - 1]; double oldLB = currState.getLowerBound(); setUpMatrix(ref newCostMatrix, Array.IndexOf(Cities, lastCityinCurrState), i, ref oldLB); double newLB = oldLB + reduceMatrix(ref newCostMatrix); ArrayList oldPath = currState.getPath(); ArrayList newPath = new ArrayList(); foreach (City c in oldPath) { newPath.Add(c); } newPath.Add(Cities[i]); TSPState childState = new TSPState(ref newPath, ref newLB, ref newCostMatrix); numOfStatesCreated++; // Prune States larger than the BSSF if (childState.getLowerBound() < bssfBound) { City firstCity = (City)childState.getPath()[0]; City lastCity = (City)childState.getPath()[childState.getPath().Count - 1]; double costToLoopBack = lastCity.costToGetTo(firstCity); // If we found a solution and it goes back from last city to first city if (childState.getPath().Count == Cities.Length && costToLoopBack != double.MaxValue) { childState.setLowerBound(childState.getLowerBound() + costToLoopBack); bssf = new TSPSolution(childState.getPath()); bssfBound = bssf.costOfRoute(); numOfSolutions++; numOfStatesNotExpanded++; // this state is not expanded because it is not put on the queue } else { // Set the priority for the state and add the new state to the queue numOfCitiesLeft = Cities.Length - childState.getPath().Count; childState.setPriority(calculateKey(numOfCitiesLeft, childState.getLowerBound())); queue.insert(childState); } } else { numOfStatesNotExpanded++; // States that are pruned are not expanded } } } currState = null; } numOfStatesNotExpanded += queue.getSize(); // if the code terminated before queue is empty, then those states never got expanded Console.WriteLine("Number of states generated: " + numOfStatesCreated); Console.WriteLine("Number of states not Expanded: " + numOfStatesNotExpanded); Console.WriteLine("Max Number of states put in queue: " + queue.getMaxNumOfItems()); end = DateTime.Now; TimeSpan diff = end - start; double seconds = diff.TotalSeconds; results[COST] = System.Convert.ToString(bssf.costOfRoute()); // load results into array here, replacing these dummy values results[TIME] = System.Convert.ToString(seconds); results[COUNT] = System.Convert.ToString(numOfSolutions); return(results); }