/// <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()
        {
            //This is used to give proportional advantages to the level of a state so that it would come off the queue before ones with lower levels.
            int priorityFactor  = 10 * Cities.Length;
            int statesCreated   = 0;
            int statesPruned    = 0;
            int maxStoredStates = 0;
            int numBssfUpdates  = 0;

            string[] results = new string[3];
            Route = new ArrayList();
            int length = Cities.Length;

            //This is the total cost matrix of all the cities to all of its connecting cities.
            double[,] costs = new double [length, length];
            Stopwatch stopWatch = new Stopwatch();
            //Binary min heap
            BinaryHeap queue = new BinaryHeap();
            //This is the upperbound of the BSSF
            double upperBound = double.PositiveInfinity;

            stopWatch.Start();

            /*
             * Iterate through the cities and fill up the total cost matrix.
             * Time comp: O(n^2)
             * Space comp: O(n^2)
             */
            for (int row = 0; row < length; row++)
            {
                for (int col = 0; col < length; col++)
                {
                    if (row == col)
                    {
                        costs[row, col] = double.PositiveInfinity;
                    }
                    else
                    {
                        costs[row, col] = Cities[row].costToGetTo(Cities[col]);
                    }
                }
            }

            /* Do greedy algorithm starting from every city and save the best to get BSSF.
             * BestRoute will save the route of the best greedy solution and upperBound will save the best upperbound.
             * Time comp: O(n^3) because we do the greedy algorithm (O(n^2)) for every city
             * Space comp: O(n^2) because we only have to store the data for each iteration of the greedy algorithm
             */
            ArrayList bestRoute = new ArrayList();

            for (int city = 0; city < length; city++)
            {
                ArrayList currRoute = new ArrayList();
                currRoute.Add(city);
                int    startPosition        = city;
                double currGreedyUpperbound = getGreedyUpperbound(city, city, costs, currRoute, length, 0);
                if (currGreedyUpperbound < upperBound)
                {
                    upperBound = currGreedyUpperbound;
                    bestRoute.Clear();
                    bestRoute = currRoute;
                }
            }

            //count = how many solutions we found.
            int count = 1;

            //First we make the starting state and add it to the priority queue.
            int[] state1_tour = new int[1];
            state1_tour[0] = 0;
            State state0 = new State(0, 1, costs, 0, state1_tour, length);

            statesCreated++;
            queue.AddToAllNodes(state0);

            /* Add the first state to the priority queue.
             * Time comp: O(nlogn)
             */
            queue.Add(state0, state0.lowerBound - priorityFactor * state0.level * state0.level);

            /* Go through the best greedy route and update the bssf object so that we have an initial BSSF to compare the starting state's children to.
             * Time comp: O(n) because we go through it once for every city
             * Space comp: O(n) can't get more than the number of cities
             */
            foreach (int i in bestRoute)
            {
                Route.Add(Cities[i]);
            }
            bssf = new TSPSolution(Route);

            //If the greedy solution's upperbound is equal to the first state's lowerbound, we know that the calculated greedy solution is the optimal solution.
            if (upperBound == state0.lowerBound)
            {
                Console.WriteLine("States Created: " + statesCreated);
                Console.WriteLine("States Pruned: " + statesPruned);
                Console.WriteLine("Max # of Stored States at a given time: " + maxStoredStates);
                Console.WriteLine("Times BSSF Updated: " + numBssfUpdates);
                Console.WriteLine("Priority Factor: " + priorityFactor);
                results[COST]  = costOfBssf().ToString();
                results[TIME]  = stopWatch.Elapsed.ToString();
                results[COUNT] = count.ToString();
                return(results);
            }

            //while loop that stops once the time is up or once we have been through the whole state tree
            // Time complexity of the branch is O(n^2 * (n-1)!)

            while (stopWatch.ElapsedMilliseconds < time_limit && queue.Count > 0)
            {
                //store the max number of stored states at a given time
                if (queue.Count > maxStoredStates)
                {
                    maxStoredStates = queue.Count;
                }

                /* Grabs the smallest priority state from the queue.
                 * Time comp: O(nlogn)
                 */
                State state = queue.ExtractMin();

                //If the BSSF solution's upperbound is equal to the current state's lowerbound, we know that the calculated BSSF solution is the optimal solution.
                if (state.lowerBound == upperBound)
                {
                    Console.WriteLine("States Created: " + statesCreated);
                    Console.WriteLine("States Pruned: " + statesPruned);
                    Console.WriteLine("Max # of Stored States at a given time: " + maxStoredStates);
                    Console.WriteLine("Times BSSF Updated: " + numBssfUpdates);
                    Console.WriteLine("Priority Factor: " + priorityFactor);
                    results[COST]  = costOfBssf().ToString();
                    results[TIME]  = stopWatch.Elapsed.ToString();
                    results[COUNT] = count.ToString();
                    return(results);
                }

                //If the BSSF solution's upperbound is lower than the current state's lowerbound, we know that we can prune it and not expand its children.
                else if (state.lowerBound > upperBound)
                {
                    statesPruned++;
                }
                else //Let's get the state's children!
                {
                    //If we're at a leaf and its bound is less than the current best so far bound, then we make this solution the new BSSF.
                    if (state.tour.Length == length + 1)
                    {
                        count++;
                        if (state.lowerBound < upperBound)
                        {
                            /* Go through the best route so far and update the bssf object.
                             * Time comp: O(n) because we go through it once for every city
                             * Space comp: O(n) can't get more than the number of cities
                             */
                            Route = new ArrayList();
                            for (int i = 0; i < state.tour.Length; i++)
                            {
                                Route.Add(Cities[state.tour[i]]);
                            }
                            bssf = new TSPSolution(Route);
                            numBssfUpdates++;
                            upperBound = state.lowerBound;
                        }
                    }
                    else //Get children and add them to the queue if they are not prunable.
                    {
                        /* Go through all of the current state's children while also getting the tour for each child.
                         * Time comp: O(n^2) because we calculate the tour for each children for the state
                         * Space comp: O(n^2) because we store all the children and their tour
                         */
                        for (int col = 0; col < length; col++)
                        {
                            double currCost = costs[state.pointsIndex, col];
                            if (!currCost.Equals(double.PositiveInfinity))
                            {
                                //Get the tour for the child.
                                int[] currTour = new int[state.level + 1];
                                for (int i = 0; i < state.tour.Length; i++)
                                {
                                    currTour[i] = state.tour[i];
                                }
                                currTour[state.level] = col;

                                //Build a state for the child.
                                State childState = new State(col, state.level + 1, state.cost, state.lowerBound, currTour, length);
                                statesCreated++;
                                queue.AddToAllNodes(childState);

                                //If it has potential to become the BSSF, add it to the queue so that we can expand it.
                                if (childState.lowerBound < upperBound)
                                {
                                    /* Add the first state to the priority queue.
                                     * Time comp: O(nlogn)
                                     */
                                    queue.Add(childState, childState.lowerBound - priorityFactor * childState.level * childState.level);
                                }

                                //If the BSSF solution's upperbound is lower than the current child's lowerbound, we know that we can prune it and not expand it.
                                else if (childState.lowerBound > upperBound)
                                {
                                    statesPruned++;
                                }
                            }
                        }
                    }
                }
            }

            stopWatch.Stop();

            //Either we ran out of time or we definitely have the optimal solution. Return the BSSF results.
            Console.WriteLine("States Created: " + statesCreated);
            Console.WriteLine("States Pruned: " + statesPruned);
            Console.WriteLine("Max # of Stored States at a given time: " + maxStoredStates);
            Console.WriteLine("Times BSSF Updated: " + numBssfUpdates);
            Console.WriteLine("Priority Factor: " + priorityFactor);
            results[COST]  = costOfBssf().ToString();   // load results into array here, replacing these dummy values
            results[TIME]  = stopWatch.Elapsed.ToString();
            results[COUNT] = count.ToString();
            return(results);
        }
        /////////////////////////////////////////////////////////////////////////////////////////////
        // These additional solver methods will be implemented as part of the group project.
        ////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// finds the greedy tour starting from each city and keeps the best (valid) one
        /// </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[] greedySolveProblem()
        {
            string[] results = new string[3];
            Route = new ArrayList();
            int length = Cities.Length;

            //This is the total cost matrix of all the cities to all of its connecting cities.
            double[,] costs = new double[length, length];
            Stopwatch stopWatch = new Stopwatch();
            //Binary min heap
            BinaryHeap queue = new BinaryHeap();
            //This is the upperbound of the BSSF
            double upperBound = double.PositiveInfinity;

            stopWatch.Start();

            /*
             * Iterate through the cities and fill up the total cost matrix.
             * Time comp: O(n^2)
             * Space comp: O(n^2)
             */
            for (int row = 0; row < length; row++)
            {
                for (int col = 0; col < length; col++)
                {
                    if (row == col)
                    {
                        costs[row, col] = double.PositiveInfinity;
                    }
                    else
                    {
                        costs[row, col] = Cities[row].costToGetTo(Cities[col]);
                    }
                }
            }

            /* Do greedy algorithm starting from every city and save the best to get BSSF.
             * BestRoute will save the route of the best greedy solution and upperBound will save the best upperbound.
             * Time comp: O(n^3) because we do the greedy algorithm (O(n^2)) for every city
             * Space comp: O(n^2) because we only have to store the data for each iteration of the greedy algorithm
             */
            ArrayList bestRoute = new ArrayList();

            for (int city = 0; city < length; city++)
            {
                ArrayList currRoute = new ArrayList();
                currRoute.Add(city);
                int    startPosition        = city;
                double currGreedyUpperbound = getGreedyUpperbound(city, city, costs, currRoute, length, 0);
                if (currGreedyUpperbound < upperBound)
                {
                    upperBound = currGreedyUpperbound;
                    bestRoute.Clear();
                    bestRoute = currRoute;
                }
            }

            /* Go through the best greedy route and update the bssf object so that we have an initial BSSF to compare the starting state's children to.
             * Time comp: O(n) because we go through it once for every city
             * Space comp: O(n) can't get more than the number of cities
             */
            foreach (int i in bestRoute)
            {
                Route.Add(Cities[i]);
            }

            stopWatch.Stop();
            bssf           = new TSPSolution(Route);
            results[COST]  = costOfBssf().ToString(); // load results into array here, replacing these dummy values
            results[TIME]  = stopWatch.Elapsed.ToString();
            results[COUNT] = "1";                     //*** should this be 1 or the number of greedy solutions that we find???

            return(results);
        }
示例#3
0
        /// <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();
        }