private Node getPartialRoute(Node node, int rowNum, int colNum) { double[,] updated = new double[Cities.Length, Cities.Length]; for (int i = 0; i < Cities.Length; i++) { for (int k = 0; k < Cities.Length; k++) { updated[i, k] = node.getMatrix()[i, k]; } } ArrayList partialRoute = new ArrayList(); for (int i = 0; i < node.getRoute().Count; i++) { partialRoute.Add(node.getRoute()[i]); } double newbssf = node.getbssf() + node.getMatrix()[rowNum, colNum]; for (int i = 0; i < Cities.Length; i++) { updated[rowNum, i] = double.PositiveInfinity; updated[i, colNum] = double.PositiveInfinity; } updated[rowNum, colNum] = double.PositiveInfinity; updated[colNum, rowNum] = double.PositiveInfinity; partialRoute.Add(Cities[colNum]); Node newNode = new Node(newbssf, updated, partialRoute); newNode.setNum(colNum); return(reduceMatrix(newNode)); }
private Node reduceMatrix(Node node) { double[,] updated = new double[Cities.Length, Cities.Length]; double updatedbssf = node.getbssf(); for (int i = 0; i < Cities.Length; i++) { for (int k = 0; k < Cities.Length; k++) { updated[i, k] = node.getMatrix()[i, k]; } } for (int i = 0; i < Cities.Length; i++) //for each row { double min = double.PositiveInfinity; for (int k = 0; k < Cities.Length; k++) //get minimum for each row { if (updated[i, k] < min) { min = updated[i, k]; } } if (min != 0 && min != double.PositiveInfinity) { for (int k = 0; k < Cities.Length; k++) //subtract minimum from each value { updated[i, k] -= min; } updatedbssf += min; } } for (int i = 0; i < Cities.Length; i++) //for each column { double min = double.PositiveInfinity; for (int k = 0; k < Cities.Length; k++) //get minimum for each column { if (updated[k, i] < min) { min = updated[k, i]; } } if (min != 0 && min != double.PositiveInfinity) { for (int k = 0; k < Cities.Length; k++) //subtract minimum from each value { updated[k, i] -= min; } updatedbssf += min; } } Node newNode = new Node(updatedbssf, updated, node.getRoute()); newNode.setNum(node.getNum()); return(newNode); }
/// <summary> /// solve the problem. This is the entry point for the solver when the run button is clicked /// right now it just picks a simple solution. /// </summary> public void solveProblem() { Stopwatch timer = new Stopwatch(); timer.Start(); l = Cities.Length; includeMatrix = new double[l, l]; double[,] costMatrix = new double[l, l]; //make the initial cost matrix for (int i = 0; i < l; i++) { for (int j = 0; j < l; j++) { if (i == j) { costMatrix[i, j] = double.PositiveInfinity; } else { costMatrix[i, j] = Cities[i].costToGetTo(Cities[j]); } } } //use greedy algorithm to get an initial bssf int startIndex = 0; while (bssF == double.PositiveInfinity) { bssF = greedy(costMatrix, startIndex); startIndex++; } //reduce the original cost Matrix double cost = reduceMatrix(costMatrix); //--------------------------------------------------------- //put a starter node on the heap with the reduced cost and the matrix, // an empty path, and and edgeCount of 0 //---------------------------------------------------------- BinaryHeap bh = new BinaryHeap(heapSize); int[] pathArray = new int[l]; for (int i = 0; i < l; i++) { pathArray[i] = -1; } bh.insert(new Node(cost, costMatrix, pathArray, 0)); //while queue is not empty while (bh.getQueueSize() > 0) { //since we increase the number of states whether or not they are put on the queue numStatesCreated += 2; //pop the lowest cost node off the queue Node n = bh.deleteMin(); int[] nodePath = n.getPathArray(); //this should be a valid path, we successfully included enough edges if (n.getEdgeCount() == l) { //update bssf, path, bssF = n.getKey(); bestPathSoFar = n.getPathArray(); //keep track of updates numBSSFupdates++; BinaryHeap temp = new BinaryHeap(heapSize); int preCount = bh.getQueueSize(); // go through the queue and get rid of too high LB's while (bh.getQueueSize() > 0) { Node node = bh.deleteMin(); if (node.getKey() < bssF) { temp.insert(node); } bh = temp; } //when I tried to increment numStatesPruned in the while loop it was optimized out // or something, so this is where I find out how many states I pruned int postCount = bh.getQueueSize(); numStatesPruned += preCount - postCount; } else { //was not a complete path. We now go to the method that makes an include and exclude state splitDecision(n.getMatrix(), nodePath, n.getEdgeCount()); //check to see if an include improved the bssf //if not do nothing, don't put it on the queue if (((n.getKey() + maxInclude) < bssF)) { int[] includePath = new int[l]; //must make a copy of the path array since our queue might be expanding for (int i = 0; i < includePath.Length; i++) { includePath[i] = nodePath[i]; } //includes one edge includePath[maxI] = maxJ; //we must also make a copy of the cost matrix double[,] copyIncludeMatrix = new double[l, l]; if (includeMatrix != null) { for (int i = 0; i < l; i++) { for (int j = 0; j < l; j++) { copyIncludeMatrix[i, j] = includeMatrix[i, j]; } } } Node includeNode = new Node(n.getKey() + maxInclude, copyIncludeMatrix, includePath, n.getEdgeCount() + 1); bh.insert(includeNode); } else { numStatesPruned++; } //only insert the exclude state if the lower bound is less than bssf if ((n.getKey() + maxExclude < bssF) && (bh.getQueueSize() < queueLimit)) { //save some time by just using the parents' path and almost the parent's cost matrix n.getMatrix()[maxI, maxJ] = double.PositiveInfinity; reduceMatrix(n.getMatrix()); Node excludeNode = new Node(n.getKey() + maxExclude, n.getMatrix(), nodePath, n.getEdgeCount()); bh.insert(excludeNode); } else { numStatesPruned++; } } //keep track of the highest number of stored states if (bh.getQueueSize() > maxQueueSize) { maxQueueSize = bh.getQueueSize(); } //time out after 30 seconds if (timer.ElapsedMilliseconds > 30000) { numStatesPruned = numStatesCreated - bh.getQueueSize(); break; } } //------------------------------------------------------------------ //report the route and bssf just like in the demo, and time elapsed //------------------------------------------------------------------ Route = new ArrayList(); int city = 0; int count = 0; Route.Add(Cities[0]); while (count != l) { city = bestPathSoFar[city]; Route.Add(Cities[city]); count++; } // call this the best solution so far. bssf is the route that will be drawn by the Draw method. bssf = new TSPSolution(Route); // update the cost of the tour. //some values we needed for the table Program.MainForm.tbCostOfTour.Text = " " + bssf.costOfRoute() + " " + maxQueueSize + " " + numBSSFupdates + " " + numStatesCreated + " " + numStatesPruned + " "; //report the time elapsed timer.Stop(); Program.MainForm.tbElapsedTime.Text = timer.Elapsed.ToString(); // do a refresh. Program.MainForm.Invalidate(); }
/// <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]; string[] origResults = defaultSolveProblem(); double origbssf = Convert.ToDouble(origResults[COST]); double[,] originalDistances = new double[Cities.Length, Cities.Length]; List <Node> todoNodes = new List <Node>(); int numSolutions = 0; int numChildStates = 0; int statesPruned = 0; int numBSSF = 0; int maxNumOfStates = 0; double bestSolution = origbssf; ArrayList routeSolution = new ArrayList(); for (int i = 0; i < Cities.Length; i++) //make original matrix from default route { for (int k = 0; k < Cities.Length; k++) { if (i == k) { originalDistances[i, k] = double.PositiveInfinity; } else { originalDistances[i, k] = Cities[i].costToGetTo(Cities[k]); } } } Stopwatch timer = new Stopwatch(); timer.Start(); routeSolution.Add(Cities[0]); Node startNode = new Node(0, originalDistances, routeSolution); //put nodes in queue todoNodes.Add(reduceMatrix(startNode)); while (todoNodes.Count > 0) //while the list is not empty { if (timer.Elapsed.TotalSeconds >= TIME_LIMIT) { break; } Node node = todoNodes[0]; //get first element if (node.getRoute().Count == Cities.Length) //if a leaf node { numSolutions++; TSPSolution sol = new TSPSolution(node.getRoute()); if (sol.costOfRoute() < bestSolution) //reset the best solution if it is lower { bestSolution = sol.costOfRoute(); routeSolution = node.getRoute(); numBSSF++; } } else //if not a leaf node { if (node.getbssf() <= bestSolution) //pruning not needed { for (int i = 1; i < Cities.Length; i++) { if (node.getMatrix()[node.getNum(), i] != double.PositiveInfinity) { todoNodes.Add(getPartialRoute(node, node.getNum(), i)); numChildStates++; } } } else { statesPruned++; } } if (todoNodes.Count > maxNumOfStates) { maxNumOfStates = todoNodes.Count; } todoNodes.RemoveAt(0); //remove the node when finished calculating todoNodes.Sort(); //sorting based on priority } timer.Stop(); bssf = new TSPSolution(routeSolution); Console.WriteLine("Number of child states is " + numChildStates); Console.WriteLine("Number of states pruned is " + statesPruned); Console.WriteLine("Number of bssf updates is " + numBSSF); Console.WriteLine("Number of stored states is " + maxNumOfStates); results[COST] = bssf.costOfRoute().ToString(); // load results into array here, replacing these dummy values results[TIME] = timer.Elapsed.ToString(); results[COUNT] = numSolutions.ToString(); return(results); }