// creating a child node is O(n^2) for both time and space complexity public DistMatrix(int toNode, DistMatrix parent) { // take in the index from the parent this.maxIndex = parent.maxIndex; // add the cost of moving from the previous node to the current node this.b = parent.b + parent.m[parent.currNode * maxIndex + toNode]; // create a new m matrix and copy over all the values this.m = new double[maxIndex * maxIndex]; for (int i = 0; i < m.Length; i++) { this.m[i] = parent.m[i]; } // create a new visited list and copy those values this.visited = new List <int>(); for (int i = 0; i < parent.visited.Count; i++) { this.visited.Add(parent.visited[i]); } // update the visited list this.visited.Add(toNode); this.toNode = toNode; this.currNode = toNode; this.fromNode = parent.currNode; // mark infinity on the column for the node going to (the child) markToNode(); // mark infinities on the row for the node coming from (the parent) markFromNode(); // mark the awkward diagonal cell in the matrix as inifinity markFromToNode(); // make sure there are still 0 values in row/column updateMatrix(); }
/// <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> // This algorithm implements a greedy approach, 10,000 random attempts, and the branch and bound approach with pruning of leaf // nodes that have values grater than the current BSSF. // O = greedy + 10,000*random + branch and bound // = O(n^3 + 10000*n + (2^n)*(n^2) = O((n^2)*(2^n)) public string[] bBSolveProblem() { // initialize the variables int count = 0; string[] results = new string[3]; int[] perm = new int[Cities.Length]; Route = new ArrayList(); Stopwatch timer = new Stopwatch(); int maxStates = 0; int totalStates = 0; int statesPruned = 0; // start the timer timer.Start(); // start BSSF as positive infinity; double currSol = Double.PositiveInfinity; // see if the BSSF updates with the greedy algorithm // Finding greedy solution = O(n^3) double greedySol = Convert.ToDouble(greedySolveProblem()[0]); if (greedySol < currSol) { currSol = greedySol; count += 1; } // attempt updating the BSSF with 10,000 random solutions // finding random solution = O(n) for (int cycleRand = 0; cycleRand < 10000; cycleRand++) { double tempSol = Convert.ToInt32(defaultSolveProblem()[0]); if (tempSol < currSol) { currSol = tempSol; count += 1; } } // create the List of nodes List <DistMatrix> children = new List <DistMatrix>(); // initialize the first node in the list (root node) children.Add(new DistMatrix(Cities)); // while there are nodes in the node list while (children.Count > 0) { // check if the timer has passed 60 seconds timer.Stop(); long time = timer.ElapsedMilliseconds / 1000; if (time >= 60) { // if it has passed 60 seconds, break out of the while loop break; } timer.Start(); // Find the next node - O(n) if (maxStates < children.Count) { maxStates = children.Count; } // collect the variables to find the best child node DistMatrix currNode = children[0]; double best = children[0].b; int numVisited = children[0].visited.Count; // for each child node, check if it is optimal for (int i = 0; i < children.Count; i++) { double value = children[i].b; int visited = children[i].visited.Count; double diffValue = value - best; int diffVisit = visited - numVisited; // used this little algorithm to try and balance breadth and depth if (value < best * (1 + (diffVisit / .75))) { currNode = children[i]; best = children[i].b; numVisited = children[i].visited.Count; } } // remove that node from the list of possiblities children.RemoveAt(children.IndexOf(currNode)); // if the child node has not reached the end if (!currNode.finished()) { // get the list of nodes that haven't been visited List <int> nextChildren; nextChildren = currNode.returnNodesLeft(); // create a child object for each of those nodes - O(n^2) happening 2^n times // for the worst case scenario where each leaf must be reached in the tree. // This gives a O((n^2)*(2^n)) for (int i = 0; i < nextChildren.Count; i++) { DistMatrix child = new DistMatrix(nextChildren[i], currNode); children.Add(new DistMatrix(nextChildren[i], currNode)); totalStates += 1; } } // if the node is a leaf, check if it's better than the current BSSF //O(n) to check each one, with O(n) to check if any need removing after // an update on the BSSF. else { if (currNode.b < currSol) { // if it is better, replace the current BSSF ArrayList tempRoute = buildRoute(currNode.visited); TSPSolution test = new TSPSolution(tempRoute); if (test.costOfRoute() < currSol) { Route.Clear(); Route = buildRoute(currNode.visited); bssf = null; bssf = new TSPSolution(Route); currSol = costOfBssf(); count += 1; } // filter any children that has a b greater than the current bssf. // remove those children in hopes of speeding up the algorithm } } for (int i = 0; i < children.Count; i++) { if (children[i].b > currSol) { children.RemoveAt(i); statesPruned += 1; i--; } } } // Stop the time and report the results. timer.Stop(); results[COST] = costOfBssf().ToString(); // load results array results[TIME] = timer.Elapsed.ToString(); results[COUNT] = count.ToString(); Console.WriteLine("Max States: {0}, Total States: {1}, Total Pruned: {2}", maxStates, totalStates, statesPruned); return(results); }