private TspState GreedyCalc(TspState state) { var n = state.CostMatrix.GetLength(0); while (!state.IsComplete) { TspState cheapestBranch = null; var cheapestBranchValue = double.PositiveInfinity; for (var toCity = 0; toCity < n; ++toCity) { if (!state.CanVisit(toCity)) { continue; } var testBranch = state.Visit(toCity); if (testBranch.LowerBound >= cheapestBranchValue) { continue; } cheapestBranch = testBranch; cheapestBranchValue = testBranch.LowerBound; } if (cheapestBranch == null) { return(null); } state = cheapestBranch; } return(state); }
private TspState(TspState fromState, int toCity) { _size = fromState._size; CostMatrix = (double[, ])fromState.CostMatrix.Clone(); LowerBound = fromState.LowerBound; var fromCity = fromState.CurrentCity; Debug.Assert(toCity < _size && toCity >= 0); Debug.Assert(fromCity == -1 || !double.IsPositiveInfinity(CostMatrix[fromCity, toCity]), "Navigating to illegal city"); Debug.Assert(fromState.NearComplete ? fromState.Path[0] == toCity : !fromState.Path.Contains(toCity), "Path contains illegal duplicate"); Path = fromState.Path.AppendToCopy(toCity); CurrentCity = toCity; // The cost matrix does not change when starting from nothing if (fromCity == -1) { return; } // Cannot go from toCity to CurrentCity (backwards) CostMatrix[toCity, fromCity] = double.PositiveInfinity; // The lower bound is increased by the cost of the path LowerBound += CostMatrix[fromCity, toCity]; // Nothing else can go to toCity (inf toCity column) for (var row = 0; row < _size; ++row) { CostMatrix[row, toCity] = double.PositiveInfinity; } // The current node now can't go to anything as it's no longer the head of the path (inf fromCity row) for (var col = 0; col < _size; ++col) { CostMatrix[fromCity, col] = double.PositiveInfinity; } // Reduce the new state Reduce(); }
//if (_mode == HardMode.Modes.Hard) //{ // var upperBound = double.PositiveInfinity; // for (var startCity = 0; startCity < _cities.Length; ++startCity) // { // var testState = BranchBoundCalc(baseState, startCity); // if (testState == null || testState.LowerBound >= upperBound) continue; // upperBound = testState.LowerBound; // bestState = testState; // } //} //else ///////////////////////////////////////////////////////////////////////////////////////////// // 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() { var results = new string[3]; var timer = new Stopwatch(); Debug.Assert(_cities.Length > 0); timer.Start(); var costMatrix = new TspState(_cities).CostMatrix; if (_mode == HardMode.Modes.Hard) { // CostOfBssf() is still used because new TspState reduces the matrix once var cost = double.PositiveInfinity; for (var startCity = 0; startCity < _cities.Length; ++startCity) { var thisResult = FastGreedyCalc(startCity, costMatrix); if (thisResult.Item1 >= cost) { continue; } cost = thisResult.Item1; _bssf = new TspSolution(thisResult.Item2.Select(i => _cities[i])); } } else { var cityIndexes = FastGreedyCalc(0, costMatrix); _bssf = new TspSolution(cityIndexes.Item2.Select(i => _cities[i])); } timer.Stop(); results[Cost] = CostOfBssf().ToString(CultureInfo.InvariantCulture); results[Time] = timer.Elapsed.ToString(); results[Count] = (_cities.Length + 1).ToString(); return(results); }
/// <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[] BranchBoundSolveProblem() { var results = new string[3]; var timer = new Stopwatch(); Debug.Assert(_cities.Length > 0); var baseState = new TspState(_cities); timer.Start(); var n = _cities.Length; var queue = new FastPriorityQueue <TspState>(n * n * n); baseState = baseState.Visit(0); queue.Enqueue(baseState, baseState.Heuristic()); // The current best complete route var bestState = GreedyCalc(baseState); var upperBound = bestState?.LowerBound ?? double.PositiveInfinity; var stored = 0; var pruned = 0; var maxStates = 0; var updates = 0; while (queue.Count > 0) { if (queue.Count > maxStates) { maxStates = queue.Count; } // Get best state to check based on heuristic. // Runs in O(log n) time. var state = queue.Dequeue(); // Branch for (var toCity = 0; toCity < n; ++toCity) { // If we can't visit the city, no need to consider it if (!state.CanVisit(toCity)) { continue; } var branchState = state.Visit(toCity); // Bound if (branchState.LowerBound < upperBound) { if (branchState.IsComplete) { ++updates; // On a complete instance, no need to add it to the queue. Debug.Assert(branchState.CostMatrix.Cast <double>().All(double.IsPositiveInfinity), "Cost Matrix is not all infinity"); upperBound = branchState.LowerBound; bestState = branchState; continue; } ++stored; if (queue.Count + 5 >= queue.MaxSize) { queue.Resize(queue.MaxSize * 2); } // Runs in O(log n) time queue.Enqueue(branchState, branchState.Heuristic()); } else { ++pruned; } } // Abandon ship and give the best result otherwise if (timer.ElapsedMilliseconds > _timeLimit) { break; } } timer.Stop(); Debug.Assert(bestState != null && bestState.IsComplete); _bssf = StateToSolution(bestState); results[Cost] = CostOfBssf().ToString(CultureInfo.InvariantCulture); // load results array results[Time] = timer.Elapsed.ToString(); results[Count] = $"{maxStates}/{updates}/{stored}/{pruned}"; return(results); }
private TspSolution StateToSolution(TspState state) { return(new TspSolution(state.Path.Select(cityIndex => _cities[cityIndex]))); }