/* * procedure Greedy Randomized Construction(α, Seed) * 1 Solution ← ∅; * 2 Initialize the candidate set: C ← E; * 3 Evaluate the incremental cost c(e) for all e ∈ C; * 4 while C 6= ∅ do * 5 cMin ← min{c(e) | e ∈ C}; * 6 cMax ← max{c(e) | e ∈ C}; * 7 RCL ← {e ∈ C | c(e) ≤ cMin + α(cMax − cMin)}; * 8 Select an element s from the RCL at random; * 9 Solution ← Solution ∪ {s}; * 10 Update the candidate set C; * 11 Reevaluate the incremental costs c(e) for all e ∈ C; * 12 end; * 13 return Solution; * end Greedy Randomized Construction. */ public static VCRPSolution GreedyRandomizedSolution(double alpha, int seed) { // Instanciate solution VCRPSolution solution = new VCRPSolution(VCRPInstance.n_vehicles, VCRPInstance.n_nodes); // Get all clients as candidates for candidatesSet; Cost(Depot -> Client) List <Tuple <int, int, double> > candidatesSet = getCandidates(); // Restricted Candidate List (RCL) List <Tuple <int, int, double> > RCL = new List <Tuple <int, int, double> >(); // Instanciate random Random rand = new Random(); while (candidatesSet.Count != 0) { // Evaluate Min and Max cost of the candidatesSet Tuple <double, double> cMinMax = getcMinMax(candidatesSet); double cMin = cMinMax.Item1; double cMax = cMinMax.Item2; // Creates new RCL foreach (var candidate in candidatesSet) { double currentCost = candidate.Item3; if (currentCost <= cMin + alpha * (cMax - cMin)) { RCL.Add(candidate); } } // Select a random candidate from RCL ; Candidate = <Client,Vehicle,Cost> int chosenIndex = rand.Next(0, RCL.Count - 1); Tuple <int, int, double> chosenCandidate = RCL.ElementAt(chosenIndex); // Check if it's feasible to insert the candidate in the solution insertSolutionElement(chosenCandidate, solution); // Remove chosen candidate from candidateSet candidatesSet.RemoveAll(candidate => candidate.Item1 == chosenCandidate.Item1); // List of elements to update cost, where route is the same of the chosenCandidate IEnumerable <Tuple <int, int, double> > candidatesToUpdate = candidatesSet.Where(candidate => candidate.Item2 == chosenCandidate.Item2); List <Tuple <int, int, double> > candidatesToInsert = new List <Tuple <int, int, double> >(); foreach (var candidate in candidatesToUpdate) { double newCost = VCRPInstance.weight_matrix[chosenCandidate.Item1, candidate.Item1]; candidatesToInsert.Add(Tuple.Create(candidate.Item1, candidate.Item2, newCost)); } candidatesSet.RemoveAll(candidate => candidate.Item2 == chosenCandidate.Item2); candidatesSet.AddRange(candidatesToInsert); // Clear RCL RCL.Clear(); } // Routes must return to depot addDepotToRoutes(solution); return(solution); }
public static Tuple <double, int> Inter10(VCRPSolution solution, int client, int brokenRouteIndex, int k) { //passa c1 de k1 para k2, na melhor posicao possivel. Nao aplica realmente, só calcula List <int> modifiedRoute = solution.routes[k]; int clientPosition = solution.routes[brokenRouteIndex].IndexOf(client); Tuple <double, int> move = new Tuple <double, int>(double.MaxValue, -1); // Get best client to insert before for (int i = 1; i < modifiedRoute.Count() - 1; i++) { double cost = solution.cost; // Remove cost of old link cost -= (VCRPInstance.weight_matrix[modifiedRoute.ElementAt(i - 1), modifiedRoute.ElementAt(i)]); // Costs of inserting client cost += (VCRPInstance.weight_matrix[modifiedRoute.ElementAt(i - 1), client]); cost += (VCRPInstance.weight_matrix[client, modifiedRoute.ElementAt(i)]); // New vert in the broken route cost += (VCRPInstance.weight_matrix[solution.routes[brokenRouteIndex][clientPosition - 1], solution.routes[brokenRouteIndex][clientPosition + 1]]); // Costs of removing client from broken route cost -= (VCRPInstance.weight_matrix[solution.routes[brokenRouteIndex].ElementAt(clientPosition - 1), client]); cost -= (VCRPInstance.weight_matrix[client, solution.routes[brokenRouteIndex].ElementAt(clientPosition + 1)]); if (cost < move.Item1) { move = new Tuple <double, int>(cost, i); } } return(move); }
private static void updateBestSolution(VCRPSolution bestSolution, VCRPSolution newSolution) { if (bestSolution.cost > newSolution.cost) { bestSolution.cost = newSolution.cost; bestSolution.routes = newSolution.routes; } }
// Force routes to return to depot and add the corresponding cost public static void addDepotToRoutes(VCRPSolution solution) { for (int k = 0; k < VCRPInstance.n_vehicles; k++) { int lastClient = solution.routes[k].Last(); solution.routes[k].Add(VCRPInstance.depot); solution.cost += VCRPInstance.weight_matrix[lastClient, VCRPInstance.depot]; } }
public static void LocalSearch(VCRPSolution solution) { // Execute 2-opt for each route of the current solution double newTotalCost = 0; for (int k = 0; k < VCRPInstance.n_vehicles; k++) { newTotalCost += TwoOpt(solution.routes[k]); } solution.cost = newTotalCost; }
static void Main(string[] args) { Console.Clear(); Console.WriteLine("========================================================================="); Console.WriteLine("Capacitated Vehicle Routing Problem (CVRP) with GRASP \n Mateus Riad (228157) \n Ricardo Pires (208784) \n"); Console.WriteLine("(All the instances can be found inside this project, dir: instances) \n"); Console.WriteLine("========================================================================="); Console.WriteLine("Name of the instance to execute: "); string fileName = Console.ReadLine(); // Pega as informaçoes do arquivo de instancia passado Parser.parserFile(fileName); Console.WriteLine("=== EXECUTING GRASP ===\n"); // Inicializa timer Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); // GRASP VCRPInstance instance = new VCRPInstance(); VCRPSolution bestSolution = Grasp.Execute(instance); stopWatch.Stop(); Console.WriteLine("=== SOLUTION FOUND ===\n"); Console.WriteLine("Best solution found: " + bestSolution.cost); Console.WriteLine("Executiond Time:" + stopWatch.Elapsed + " seconds."); for (int k = 0; k < VCRPInstance.n_vehicles; k++) { Console.Write("Rota " + k + ":"); List <int> range = bestSolution.routes[k]; foreach (int value in range) { Console.Write(value + " - "); } Console.WriteLine("\n"); } string wait = Console.ReadLine(); }
public static VCRPSolution Execute(VCRPInstance instance) { /* * procedure GRASP(Max Iterations,Seed) * 1 Read Input(); * 2 for k = 1, . . . , Max Iterations do * 3 Solution ← Greedy Randomized Construction(Seed); * 4 Solution ← Local Search(Solution); * 5 Update Solution(Solution,Best Solution); * 6 end; * 7 return Best Solution; * end GRASP. */ // Grasp Parameters int maxIterations = 200; double alpha = 1; int seed = 0; // ? // Initialize best solution with worst cost const int worstSolution = UInt16.MaxValue; VCRPSolution bestSolution = new VCRPSolution(VCRPInstance.n_vehicles, VCRPInstance.n_nodes); bestSolution.cost = worstSolution; // Solution instance used for grasp iterations VCRPSolution currentSolution; for (int k = 0; k < maxIterations; k++) { // Constructive Heuristic Phase currentSolution = GreedyRandomizedSolution(alpha, seed); Console.WriteLine("=============================================\n"); Console.WriteLine("GREEDY - INITIAL SOLUTION\n"); for (int v = 0; v < VCRPInstance.n_vehicles; v++) { Console.Write("Rota " + v + ": "); List <int> range = currentSolution.routes[v]; foreach (int value in range) { Console.Write(value + " - "); } Console.WriteLine("\n"); } Console.WriteLine("Custo total: " + currentSolution.cost + "\n"); // Check and repair solution in case it's not feasible (YET!) GreedyPostProcessing(currentSolution); Console.WriteLine("=============================================\n"); Console.WriteLine("GREEDY - POST PROCESSING\n"); for (int v = 0; v < VCRPInstance.n_vehicles; v++) { Console.Write("Rota " + v + ":"); List <int> range = currentSolution.routes[v]; foreach (int value in range) { Console.Write(value + " - "); } Console.WriteLine(getRouteDemand(currentSolution.routes[v])); Console.WriteLine("\n"); } Console.WriteLine("Custo total: " + currentSolution.cost + "\n"); // Local Search //LocalSearch(currentSolution); /* * Console.WriteLine("=============================================\n"); * Console.WriteLine("AFTER LOCAL SEARCH\n"); * for (int v = 0; v < VCRPInstance.n_vehicles; v++) * { * Console.Write("Rota " + v + ": "); * List<int> range = currentSolution.routes[v]; * foreach (int value in range) * { * Console.Write(value + " - "); * } * Console.WriteLine("\n"); * }*/ // Check and update best solution updateBestSolution(bestSolution, currentSolution); } return(bestSolution); }
// Insert new element in the current solution public static void insertSolutionElement(Tuple <int, int, double> currentCandidate, VCRPSolution solution) { int client = currentCandidate.Item1; int route = currentCandidate.Item2; double cost = currentCandidate.Item3; solution.routes[route].Add(client); solution.cost += cost; }
// Check if solution is feasible and repair it if not public static void GreedyPostProcessing(VCRPSolution solution) { // List contains the total sum of demands in each route int[] routesDemand = new int[VCRPInstance.n_vehicles]; // List of unfeasible routes and it's excess over capacity List <int> brokenRoutes = new List <int>(); int[] excessError = new int[VCRPInstance.n_vehicles]; // get sum of client's demand for each route for (int k = 0; k < VCRPInstance.n_vehicles; k++) { int currentRouteDemand = getRouteDemand(solution.routes[k]); Console.Write(currentRouteDemand + " - "); routesDemand[k] = currentRouteDemand; if (currentRouteDemand > VCRPInstance.g_capacity) { brokenRoutes.Add(k); excessError[k] = currentRouteDemand - VCRPInstance.g_capacity; } } // TODO: Kill them! Tuple <double, int, int, int, int> bestFact = new Tuple <double, int, int, int, int>(double.MaxValue, -1, -1, -1, -1); Tuple <double, int, int, int, int> bestF = new Tuple <double, int, int, int, int>(double.MaxValue, -1, -1, -1, -1); // If there is any item in brokenRoutes, current solution is not feasible; // Then repair it; while (brokenRoutes.Count != 0) { Console.WriteLine("Count: " + brokenRoutes.Count); // Find movement to improve feasibility of current solution foreach (int brokenRouteIndex in brokenRoutes) { Console.WriteLine("Broken: " + brokenRouteIndex); for (int k = 0; k < VCRPInstance.n_vehicles; k++) { int a = routesDemand[k]; // check movement to another route if (brokenRouteIndex != k) { // One of the clients can reduce excessError? foreach (int client in solution.routes[brokenRouteIndex].Where(x => x > 0)) { int b = VCRPInstance.nodes[client].demand; // Check if the demand of a client inside a broken route can be inserted in a feasible route without breaking it if (VCRPInstance.nodes[client].demand + routesDemand[k] <= VCRPInstance.g_capacity) { if (routesDemand[brokenRouteIndex] - VCRPInstance.nodes[client].demand <= VCRPInstance.g_capacity) { // Get best movement Tuple <double, int> movement = Inter10(solution, client, brokenRouteIndex, k); if (bestFact.Item1 > movement.Item1) { bestFact = new Tuple <double, int, int, int, int>(movement.Item1, movement.Item2, client, k, brokenRouteIndex); } } } if (VCRPInstance.nodes[client].demand + routesDemand[k] <= VCRPInstance.g_capacity) { if (routesDemand[brokenRouteIndex] - VCRPInstance.nodes[client].demand > VCRPInstance.g_capacity) { Tuple <double, int> movement = Inter10(solution, client, brokenRouteIndex, k); // Movement reduces excess error if (bestF.Item1 > movement.Item1) { bestF = new Tuple <double, int, int, int, int>(movement.Item1, movement.Item2, client, k, brokenRouteIndex); } } } } } } } // Execute movement // There is a movement that makes broken route feasible without breaking another one if (bestFact.Item2 != -1 && brokenRoutes.Contains(bestFact.Item5)) { // Update solution and remove route from brokenRoutes Console.WriteLine("Movimento pra resolver o erro! Rota: " + bestFact.Item5); solution.cost = bestFact.Item1; // Insert client in a new route / Remove it from current solution.routes[bestFact.Item4].Insert(bestFact.Item2, bestFact.Item3); solution.routes[bestFact.Item5].Remove(bestFact.Item3); // Now, route if feasible brokenRoutes.Remove(bestFact.Item5); // Update Demands routesDemand[bestFact.Item4] += VCRPInstance.nodes[bestFact.Item3].demand; routesDemand[bestFact.Item5] -= VCRPInstance.nodes[bestFact.Item3].demand; bestFact = new Tuple <double, int, int, int, int>(double.MaxValue, -1, -1, -1, -1); } else if (bestF.Item2 != -1) { Console.WriteLine("Movimento pra reduzir o erro rota:" + bestF.Item5); solution.cost = bestF.Item1; // Insert client in a new route / Remove it from current solution.routes[bestF.Item4].Insert(bestF.Item2, bestF.Item3); solution.routes[bestF.Item5].Remove(bestF.Item3); // Update Demands routesDemand[bestF.Item4] += VCRPInstance.nodes[bestF.Item3].demand; routesDemand[bestF.Item5] -= VCRPInstance.nodes[bestF.Item3].demand; // Inserting in a route that reduce excess error but breaks it if (!brokenRoutes.Contains(bestF.Item4)) { brokenRoutes.Add(bestF.Item4); } bestF = new Tuple <double, int, int, int, int>(double.MaxValue, -1, -1, -1, -1); } } }