/// <summary> /// Applies inter-improvements by exchanging parts of the route(s). /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1_idx"></param> /// <param name="route2_idx"></param> /// <param name="max"></param> /// <returns></returns> public bool Improve(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int max_window = 50; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); int route1_customers = route1.Count; int route2_customers = route2.Count; double[] route1_cumul = problem.TimeCumul(route1); double[] route2_cumul = problem.TimeCumul(route2); // build all edge weights. List<Edge> route1_edges = new List<Edge>(route1.Edges()); List<Edge> route2_edges = new List<Edge>(route2.Edges()); double[] route1_weights = new double[route1_edges.Count]; for (int idx = 0; idx < route1_edges.Count; idx++) { Edge edge = route1_edges[idx]; route1_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } double[] route2_weights = new double[route2_edges.Count]; for (int idx = 0; idx < route2_edges.Count; idx++) { Edge edge = route2_edges[idx]; route2_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } List<EdgePair> route2_pairs = new List<EdgePair>(); for (int i_idx = 0; i_idx < route2_edges.Count - 2; i_idx++) { Edge i = route2_edges[i_idx]; double i_weight = route2_weights[i_idx]; double weight_before_i = route2_cumul[i_idx]; int k_idx_max = route2_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route2_edges[k_idx]; double k_weight = route2_weights[k_idx]; double weight_after_k = route2_cumul[route2_cumul.Length - 1] - route2_cumul[k_idx + 1]; double weight_between_route = route2_cumul[k_idx] - route2_cumul[i_idx + 1]; route2_pairs.Add(new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List<int>(route2.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }); } } // build all edge pairs. for (int i_idx = 0; i_idx < route1_edges.Count - 2; i_idx++) { Edge i = route1_edges[i_idx]; double i_weight = route1_weights[i_idx]; double weight_before_i = route1_cumul[i_idx]; int k_idx_max = route1_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route1_edges[k_idx]; double k_weight = route1_weights[k_idx]; double weight_after_k = route1_cumul[route1_cumul.Length - 1] - route1_cumul[k_idx + 1]; double weight_between_route = route1_cumul[k_idx] - route1_cumul[i_idx + 1]; EdgePair pair1 = new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List<int>(route1.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }; foreach (EdgePair pair2 in route2_pairs) { double existing_weight = pair1.WeightTotal + pair2.WeightTotal; //double new_weight = 0; // get first route new. double new_weight = problem.WeightMatrix[pair1.First.From][pair2.First.To]; //new_weight = first_route1_new; if (new_weight > existing_weight - 0.001) { continue; } double first_route2_new = problem.WeightMatrix[pair2.First.From][pair1.First.To]; new_weight = new_weight + first_route2_new; if (new_weight > existing_weight - 0.001) { continue; } double second_route1_new = problem.WeightMatrix[pair1.Second.From][pair2.Second.To]; new_weight = new_weight + second_route1_new; if (new_weight > existing_weight - 0.001) { continue; } double second_route2_new = problem.WeightMatrix[pair2.Second.From][pair1.Second.To]; new_weight = new_weight + second_route2_new; if (new_weight < existing_weight - 0.001) { // there is a decrease in total weight; check bounds. double route1_weight = pair1.WeightBefore + pair2.WeightBetween + pair1.WeightAfter; double route2_weight = pair2.WeightBefore + pair1.WeightBetween + pair2.WeightAfter; // calculate the maximum. int route1_customers_between = pair1.CustomersBetween; int route2_customers_between = pair1.CustomersBetween; route1_weight = problem.Time(route1_weight, route1_customers - route1_customers_between + route2_customers_between); route2_weight = problem.Time(route2_weight, route2_customers - route2_customers_between + route1_customers_between); if (route1_weight < max && route2_weight < max) { // MaxTimeSolution solution_copy = solution.Clone() as MaxTimeSolution; List<int> route1_between = pair1.Between; List<int> route2_between = pair2.Between; route1.ReplaceEdgeFrom(pair1.First.From, pair1.Second.To); route2.ReplaceEdgeFrom(pair2.First.From, pair2.Second.To); int previous = pair1.First.From; for (int idx = 0; idx < route2_between.Count; idx++) { route1.ReplaceEdgeFrom(previous, route2_between[idx]); previous = route2_between[idx]; } route1.ReplaceEdgeFrom(previous, pair1.Second.To); previous = pair2.First.From; for (int idx = 0; idx < route1_between.Count; idx++) { route2.ReplaceEdgeFrom(previous, route1_between[idx]); previous = route1_between[idx]; } route2.ReplaceEdgeFrom(previous, pair2.Second.To); if (!solution.IsValid()) { throw new Exception(); } return true; } } } } } return false; }
/// <summary> /// Tries to relocate customers using the source the same as the CrossExchange but places customers using cheapest insertion. /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1_idx"></param> /// <param name="route2_idx"></param> /// <param name="max"></param> /// <returns></returns> public bool Improve(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int max_window = 10; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); double total_before = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); int route1_customers = route1.Count; int route2_customers = route2.Count; double[] route1_cumul = problem.TimeCumul(route1); double[] route2_cumul = problem.TimeCumul(route2); // build all edge weights. List <Edge> route1_edges = new List <Edge>(route1.Edges()); List <Edge> route2_edges = new List <Edge>(route2.Edges()); double[] route1_weights = new double[route1_edges.Count]; for (int idx = 0; idx < route1_edges.Count; idx++) { Edge edge = route1_edges[idx]; route1_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } double[] route2_weights = new double[route2_edges.Count]; for (int idx = 0; idx < route2_edges.Count; idx++) { Edge edge = route2_edges[idx]; route2_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } List <EdgePair> route2_pairs = new List <EdgePair>(); for (int i_idx = 0; i_idx < route2_edges.Count - 2; i_idx++) { Edge i = route2_edges[i_idx]; double i_weight = route2_weights[i_idx]; double weight_before_i = route2_cumul[i_idx]; int k_idx_max = route2_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route2_edges[k_idx]; double k_weight = route2_weights[k_idx]; double weight_after_k = route2_cumul[route2_cumul.Length - 1] - route2_cumul[k_idx + 1]; double weight_between_route = route2_cumul[k_idx] - route2_cumul[i_idx + 1]; route2_pairs.Add(new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List <int>(route2.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }); } } // try to relocate all and find best pair. double route1_weight = route1_cumul[route1_cumul.Length - 1]; EdgePair best = null; CheapestInsertionResult best_result = new CheapestInsertionResult(); double best_extra = double.MaxValue; foreach (EdgePair pair2 in route2_pairs) { // calculate cheapest insertion. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route1, pair2.First.To, pair2.Second.From); double extra_route2 = problem.WeightMatrix[pair2.First.From][pair2.Second.To]; // check if the result has a net-decrease. if (result.Increase + extra_route2 < pair2.WeightTotal - 0.01) { // there is a net decrease. // calculate the real increase. double new_weight = problem.Time(route1_weight + result.Increase + pair2.WeightBetween, route1_customers + pair2.CustomersBetween); // check the max. if (new_weight < max && new_weight < best_extra) { // the route is smaller than max. best_extra = new_weight; best_result = result; best = pair2; } } } if (best != null) { if (route2.Last == best.Second.To) { //throw new Exception(); } route2.ReplaceEdgeFrom(best.First.From, best.Second.To); int previous = best_result.CustomerBefore; foreach (int customer in best.Between) { route1.ReplaceEdgeFrom(previous, customer); previous = customer; } route1.ReplaceEdgeFrom(previous, best_result.CustomerAfter); // check validity. if (!route2.IsValid()) { throw new Exception(); } if (!route1.IsValid()) { throw new Exception(); } if (route1.Count + route2.Count != route1_customers + route2_customers) { throw new Exception(); } double total_after = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); if (total_after >= total_before) { throw new Exception("this is not an improvement!"); } return(true); } return(false); }
/// <summary> /// Applies inter-improvements by exchanging parts of the route(s). /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1_idx"></param> /// <param name="route2_idx"></param> /// <param name="max"></param> /// <returns></returns> public bool Improve(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int max_window = 40; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); int route1_customers = route1.Count; int route2_customers = route2.Count; double[] route1_cumul = problem.TimeCumul(route1); double[] route2_cumul = problem.TimeCumul(route2); // build all edge weights. List <Edge> route1_edges = new List <Edge>(route1.Edges()); List <Edge> route2_edges = new List <Edge>(route2.Edges()); double[] route1_weights = new double[route1_edges.Count]; for (int idx = 0; idx < route1_edges.Count; idx++) { Edge edge = route1_edges[idx]; route1_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } double[] route2_weights = new double[route2_edges.Count]; for (int idx = 0; idx < route2_edges.Count; idx++) { Edge edge = route2_edges[idx]; route2_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } List <EdgePair> route2_pairs = new List <EdgePair>(); for (int i_idx = 0; i_idx < route2_edges.Count - 2; i_idx++) { Edge i = route2_edges[i_idx]; double i_weight = route2_weights[i_idx]; double weight_before_i = route2_cumul[i_idx]; int k_idx_max = route2_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route2_edges[k_idx]; double k_weight = route2_weights[k_idx]; double weight_after_k = route2_cumul[route2_cumul.Length - 1] - route2_cumul[k_idx + 1]; double weight_between_route = route2_cumul[k_idx] - route2_cumul[i_idx + 1]; route2_pairs.Add(new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List <int>(route2.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }); } } // build all edge pairs. for (int i_idx = 0; i_idx < route1_edges.Count - 2; i_idx++) { Edge i = route1_edges[i_idx]; double i_weight = route1_weights[i_idx]; double weight_before_i = route1_cumul[i_idx]; int k_idx_max = route1_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route1_edges[k_idx]; double k_weight = route1_weights[k_idx]; double weight_after_k = route1_cumul[route1_cumul.Length - 1] - route1_cumul[k_idx + 1]; double weight_between_route = route1_cumul[k_idx] - route1_cumul[i_idx + 1]; EdgePair pair1 = new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List <int>(route1.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }; foreach (EdgePair pair2 in route2_pairs) { double existing_weight = pair1.WeightTotal + pair2.WeightTotal; //double new_weight = 0; // get first route new. double new_weight = problem.WeightMatrix[pair1.First.From][pair2.First.To]; //new_weight = first_route1_new; if (new_weight > existing_weight - 0.001) { continue; } double first_route2_new = problem.WeightMatrix[pair2.First.From][pair1.First.To]; new_weight = new_weight + first_route2_new; if (new_weight > existing_weight - 0.001) { continue; } double second_route1_new = problem.WeightMatrix[pair1.Second.From][pair2.Second.To]; new_weight = new_weight + second_route1_new; if (new_weight > existing_weight - 0.001) { continue; } double second_route2_new = problem.WeightMatrix[pair2.Second.From][pair1.Second.To]; new_weight = new_weight + second_route2_new; if (new_weight < existing_weight - 0.001) { // there is a decrease in total weight; check bounds. double route1_weight = pair1.WeightBefore + pair2.WeightBetween + pair1.WeightAfter; double route2_weight = pair2.WeightBefore + pair1.WeightBetween + pair2.WeightAfter; // calculate the maximum. int route1_customers_between = pair1.CustomersBetween; int route2_customers_between = pair1.CustomersBetween; route1_weight = problem.Time(route1_weight, route1_customers - route1_customers_between + route2_customers_between); route2_weight = problem.Time(route2_weight, route2_customers - route2_customers_between + route1_customers_between); if (route1_weight < max && route2_weight < max) { MaxTimeSolution solution_copy = solution.Clone() as MaxTimeSolution; List <int> route1_between = pair1.Between; List <int> route2_between = pair2.Between; route1.ReplaceEdgeFrom(pair1.First.From, pair1.Second.To); route2.ReplaceEdgeFrom(pair2.First.From, pair2.Second.To); int previous = pair1.First.From; for (int idx = 0; idx < route2_between.Count; idx++) { route1.ReplaceEdgeFrom(previous, route2_between[idx]); previous = route2_between[idx]; } route1.ReplaceEdgeFrom(previous, pair1.Second.To); previous = pair2.First.From; for (int idx = 0; idx < route1_between.Count; idx++) { route2.ReplaceEdgeFrom(previous, route1_between[idx]); previous = route1_between[idx]; } route2.ReplaceEdgeFrom(previous, pair2.Second.To); if (!solution.IsValid()) { throw new Exception(); } return(true); } } } } } return(false); }
/// <summary> /// Tries to relocate customers using the source the same as the CrossExchange but places customers using cheapest insertion. /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1_idx"></param> /// <param name="route2_idx"></param> /// <param name="max"></param> /// <returns></returns> public bool Improve(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int max_window = 5; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); double total_before = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); int route1_customers = route1.Count; int route2_customers = route2.Count; double[] route1_cumul = problem.TimeCumul(route1); double[] route2_cumul = problem.TimeCumul(route2); // build all edge weights. List<Edge> route1_edges = new List<Edge>(route1.Edges()); List<Edge> route2_edges = new List<Edge>(route2.Edges()); double[] route1_weights = new double[route1_edges.Count]; for (int idx = 0; idx < route1_edges.Count; idx++) { Edge edge = route1_edges[idx]; route1_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } double[] route2_weights = new double[route2_edges.Count]; for (int idx = 0; idx < route2_edges.Count; idx++) { Edge edge = route2_edges[idx]; route2_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } List<EdgePair> route2_pairs = new List<EdgePair>(); for (int i_idx = 0; i_idx < route2_edges.Count - 2; i_idx++) { Edge i = route2_edges[i_idx]; double i_weight = route2_weights[i_idx]; double weight_before_i = route2_cumul[i_idx]; int k_idx_max = route2_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route2_edges[k_idx]; double k_weight = route2_weights[k_idx]; double weight_after_k = route2_cumul[route2_cumul.Length - 1] - route2_cumul[k_idx + 1]; double weight_between_route = route2_cumul[k_idx] - route2_cumul[i_idx + 1]; route2_pairs.Add(new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List<int>(route2.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }); } } // try to relocate all and find best pair. double route1_weight = route1_cumul[route1_cumul.Length - 1]; EdgePair best = null; CheapestInsertionResult best_result = new CheapestInsertionResult(); double best_extra = double.MaxValue; foreach (EdgePair pair2 in route2_pairs) { // calculate cheapest insertion. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route1, pair2.First.To, pair2.Second.From); double extra_route2 = problem.WeightMatrix[pair2.First.From][pair2.Second.To]; // check if the result has a net-decrease. if (result.Increase + extra_route2 < pair2.WeightTotal - 0.01) { // there is a net decrease. // calculate the real increase. double new_weight = problem.Time(route1_weight + result.Increase + pair2.WeightBetween, route1_customers + pair2.CustomersBetween); // check the max. if (new_weight < max && new_weight < best_extra) { // the route is smaller than max. best_extra = new_weight; best_result = result; best = pair2; } } } if (best != null) { if (route2.Last == best.Second.To) { //throw new Exception(); } route2.ReplaceEdgeFrom(best.First.From, best.Second.To); int previous = best_result.CustomerBefore; foreach (int customer in best.Between) { route1.ReplaceEdgeFrom(previous, customer); previous = customer; } route1.ReplaceEdgeFrom(previous, best_result.CustomerAfter); // check validity. if (!route2.IsValid()) { throw new Exception(); } if (!route1.IsValid()) { throw new Exception(); } if (route1.Count + route2.Count != route1_customers + route2_customers) { throw new Exception(); } double total_after = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); if (total_after >= total_before) { throw new Exception("this is not an improvement!"); } return true; } return false; }