/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customers"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, ICollection <int> customers) { IInsertionCosts costs = new BinaryHeapInsertionCosts(); return(CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers, costs)); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the solution. var solution = new MaxTimeSolution(problem.Size); // keep placing customer until none are left. var customers = new List <int>(problem.Customers); customers.RemoveAt(0); double max = problem.Max.Value - (problem.Max.Value * _deltaPercentage); // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); double percentage = _thresholdPercentage; while (customers.Count > 0) { //// try and distribute the remaining customers if there are only a few left. //if (customers.Count < problem.Size * percentage) //{ // bool succes = true; // while (succes && customers.Count > 0) // { // succes = false; // CheapestInsertionResult best = new CheapestInsertionResult(); // best.Increase = float.MaxValue; // int best_idx = -1; // for (int route_idx = 0; route_idx < solution.Count; route_idx++) // { // IRoute route = solution.Route(route_idx); // CheapestInsertionResult result = // CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); // if (best.Increase > result.Increase) // { // best = result; // best_idx = route_idx; // } // } // IRoute best_route = solution.Route(best_idx); // double route_time = problem.Time(best_route); // if (route_time + best.Increase < max) // { // insert the customer. // best_route.InsertAfter(best.CustomerBefore, best.Customer); // customers.Remove(best.Customer); // this.Improve(problem, solution, max, best_idx); // succes = true; // } // } //} // select a customer using some heuristic. int customer = this.SelectSeed(problem, problem.MaxTimeCalculator, solution, customers); customers.Remove(customer); // start a route r. IRoute currentRoute = solution.Add(customer); solution[solution.Count - 1] = 0; while (customers.Count > 0) { // calculate the best placement. CheapestInsertionResult result; if (_useSeedCost) { // use the seed cost; the cost to the seed customer. result = CheapestInsertionHelper.CalculateBestPlacement(problem, currentRoute, customers, customer, 0.7); // calculate the 'real' increase. result.Increase = (problem.WeightMatrix[result.CustomerBefore][result.Customer] + problem.WeightMatrix[result.Customer][result.CustomerAfter]) - problem.WeightMatrix[result.CustomerBefore][result.CustomerAfter]; } else { // just use cheapest insertion. result = CheapestInsertionHelper.CalculateBestPlacement(problem, currentRoute, customers, costs); } // calculate the new weight. solution[solution.Count - 1] = problem.Time(solution.Route(solution.Count - 1)); double potentialWeight = problem.MaxTimeCalculator.CalculateOneRouteIncrease(solution[solution.Count - 1], result.Increase); // cram as many customers into one route as possible. if (potentialWeight < max) { // insert the customer, it is customers.Remove(result.Customer); currentRoute.InsertAfter(result.CustomerBefore, result.Customer); // free some memory in the costs list. costs.Remove(result.CustomerBefore, result.CustomerAfter); // update the cost of the route. solution[solution.Count - 1] = potentialWeight; // improve if needed. if (((problem.Size - customers.Count) % _k) == 0) { // an improvement is decided. // apply the inter-route improvements. var copy = (solution.Clone() as MaxTimeSolution); int countBefore = solution.Route(solution.Count - 1).Count; solution[solution.Count - 1] = this.ImproveIntraRoute(problem, solution.Route(solution.Count - 1), solution[solution.Count - 1]); if (!solution.IsValid()) { throw new Exception(); } int countAfter = solution.Route(solution.Count - 1).Count; if (countAfter != countBefore) { throw new Exception(); } // also to the inter-improvements. currentRoute = this.Improve(problem, solution, max, solution.Count - 1); } } else {// ok we are done! this.Improve(problem, solution, max, solution.Count - 1); // break the route. break; } } } // remove empty routes. for (int routeIdx = solution.Count - 1; routeIdx >= 0; routeIdx--) { if (solution.Route(routeIdx).IsEmpty) { solution.Remove(routeIdx); } } return(solution); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the solution. MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); // keep placing customer until none are left. List <int> customers = new List <int>(problem.Customers); double max = problem.Max.Value - (problem.Max.Value * _delta_percentage); // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); double percentage = _threshold_percentage; while (customers.Count > 0) { // try and distribute the remaining customers if there are only a few left. if (customers.Count < problem.Size * percentage) { bool succes = true; while (succes && customers.Count > 0) { succes = false; CheapestInsertionResult best = new CheapestInsertionResult(); best.Increase = float.MaxValue; int best_idx = -1; for (int route_idx = 0; route_idx < solution.Count; route_idx++) { IRoute route = solution.Route(route_idx); CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); if (best.Increase > result.Increase) { best = result; best_idx = route_idx; } } IRoute best_route = solution.Route(best_idx); double route_time = problem.Time(best_route); if (route_time + best.Increase < max) { // insert the customer. best_route.InsertAfter(best.CustomerBefore, best.Customer); customers.Remove(best.Customer); this.Improve(problem, solution, max, best_idx); succes = true; } } } // select a customer using some heuristic. if (customers.Count > 0) { // select a customer using some heuristic. int customer = -1; if (_use_seed) { // use a seeding heuristic. customer = this.SelectSeed(problem, problem.MaxTimeCalculator, solution, customers); } else { // just select a random customer. customer = customers[Math.Random.StaticRandomGenerator.Get().Generate(customers.Count)]; } customers.Remove(customer); // start a route r. IRoute current_route = solution.Add(customer); solution[solution.Count - 1] = 0; while (customers.Count > 0) { //OsmSharp.IO.Output.OutputStreamHost.WriteLine("{0}/{1} placed!", // customers.Count, problem.Size); // calculate the best placement. CheapestInsertionResult result; if (_use_seed_cost) { // use the seed cost; the cost to the seed customer. result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers, customer, _lambda); // calculate the 'real' increase. result.Increase = (problem.WeightMatrix[result.CustomerBefore][result.Customer] + problem.WeightMatrix[result.Customer][result.CustomerAfter]) - problem.WeightMatrix[result.CustomerBefore][result.CustomerAfter]; } else { // just use cheapest insertion. result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers, costs); } // calculate the new weight. solution[solution.Count - 1] = problem.Time(solution.Route(solution.Count - 1)); double potential_weight = problem.MaxTimeCalculator.CalculateOneRouteIncrease(solution[solution.Count - 1], result.Increase); // cram as many customers into one route as possible. if (potential_weight < max) { // insert the customer, it is customers.Remove(result.Customer); current_route.InsertAfter(result.CustomerBefore, result.Customer); // free some memory in the costs list. costs.Remove(result.CustomerBefore, result.CustomerAfter); // update the cost of the route. solution[solution.Count - 1] = potential_weight; // improve if needed. if (((problem.Size - customers.Count) % _k) == 0) { // an improvement is descided. // apply the inter-route improvements. int count_before = solution.Route(solution.Count - 1).Count; solution[solution.Count - 1] = this.ImproveIntraRoute(problem, current_route, solution[solution.Count - 1]); if (!solution.IsValid()) { throw new Exception(); } int count_after = solution.Route(solution.Count - 1).Count; // also to the inter-improvements. current_route = this.Improve(problem, solution, max, solution.Count - 1); } } else {// ok we are done! this.Improve(problem, solution, max, solution.Count - 1); // break the route. break; } } //else //{// ok we are done! // solution[solution.Count - 1] = this.ImproveIntraRoute(problem, // current_route, solution[solution.Count - 1]); // if (!solution.IsValid()) // { // throw new Exception(); // } // int count_after = solution.Route(solution.Count - 1).Count; // this.Improve(problem, solution, max, solution.Count - 1); // // break the route. // break; //} } } // remove empty routes. for (int route_idx = solution.Count - 1; route_idx >= 0; route_idx--) { if (solution.Route(route_idx).IsEmpty) { solution.Remove(route_idx); } } return(solution); }
/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customers"></param> /// <param name="seed_customer"></param> /// <param name="seed_customer_ratio"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, ICollection <int> customers, int seed_customer, double seed_customer_ratio) { IInsertionCosts costs = new BinaryHeapInsertionCosts(); // initialize the best placement result. CheapestInsertionResult best = new CheapestInsertionResult(); best.Increase = float.MaxValue; // loop over all customers in the route. if (route.Count > 0) { // there have to be at least two customers. IEnumerator <int> route_enumerator = route.GetEnumerator(); if (!route_enumerator.MoveNext()) { // this route is empty throw new ArgumentException("Route needs to be initialized with at least two customers!"); } int customer_before = route_enumerator.Current; int customer_after = -1; while (route_enumerator.MoveNext()) { // keep moving! customer_after = route_enumerator.Current; InsertionCost cost = costs.PopCheapest(customer_before, customer_after); bool found = false; while (!found) { // test if the costs are empty. if (cost == null) { // re-initialize the costs with all customers under consideration. foreach (int customer in customers) { float cost_quantity = (float)problem.WeightMatrix[customer_before][customer] + (float)problem.WeightMatrix[customer][customer_after] - (float)problem.WeightMatrix[customer_before][customer_after]; float seed_cost = (float)problem.WeightMatrix[seed_customer][customer] + (float)problem.WeightMatrix[customer][seed_customer]; costs.Add(customer_before, customer_after, customer, (float)(cost_quantity + (seed_customer_ratio * seed_cost))); } // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } else if (customers.Contains(cost.Customer)) { // the customer is found! found = true; } else { // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } } if (cost.Cost < best.Increase) { // the costs is better than the current cost! best = new CheapestInsertionResult() { Customer = cost.Customer, CustomerAfter = customer_after, CustomerBefore = customer_before, Increase = cost.Cost }; if (best.Increase == 0) { // immidiately return if smaller than epsilon. return(best); } } // set the after to the before. customer_before = route_enumerator.Current; } // if the round is a round try first-last. if (route.IsRound) { // the route is a round! customer_after = route.First; InsertionCost cost = costs.PopCheapest(customer_before, customer_after); bool found = false; while (!found) { // test if the costs are empty. if (cost == null) { // re-initialize the costs with all customers under consideration. foreach (int customer in customers) { float cost_quantity = (float)problem.WeightMatrix[customer_before][customer] + (float)problem.WeightMatrix[customer][customer_after] - (float)problem.WeightMatrix[customer_before][customer_after]; float seed_cost = (float)problem.WeightMatrix[seed_customer][customer] + (float)problem.WeightMatrix[customer][seed_customer]; costs.Add(customer_before, customer_after, customer, (float)(cost_quantity + (seed_customer_ratio * seed_cost))); } // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } else if (customers.Contains(cost.Customer)) { // the customer is found! found = true; } else { // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } } if (cost.Cost < best.Increase) { // the costs is better than the current cost! best = new CheapestInsertionResult() { Customer = cost.Customer, CustomerAfter = customer_after, CustomerBefore = customer_before, Increase = cost.Cost }; if (best.Increase == 0) { // immidiately return if smaller than epsilon. return(best); } } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least two customers!"); } // return result. return(best); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { MaxTimeCalculator calculator = new MaxTimeCalculator(problem); // get the seed customers. ICollection <int> seeds = _seed_selector.SelectSeeds( problem, _k); double[] weights = new double[seeds.Count]; // start the seed routes. List <int> selectable_customers = problem.Customers; MaxTimeSolution routes = new MaxTimeSolution( problem.Size, true); foreach (int seed in seeds) { routes.Add(seed); selectable_customers.Remove(seed); } if (!routes.IsValid()) { throw new Exception(); } // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); // keep looping until all customers have been placed. while (selectable_customers.Count > 0) { // try and place into every route. CheapestInsertionResult best_result = new CheapestInsertionResult(); best_result.Increase = float.MaxValue; int best_route_idx = -1; CheapestInsertionResult best_result_above_max = new CheapestInsertionResult(); best_result_above_max.Increase = float.MaxValue; int best_route_above_max_idx = -1; for (int route_idx = 0; route_idx < routes.Count; route_idx++) { IRoute current_route = routes.Route(route_idx); // choose the next customer. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, selectable_customers, costs); if (result.Customer == result.CustomerAfter) { throw new Exception(); } // get the current weight double weight = weights[route_idx]; if (result.Increase < best_result.Increase) { if (weight + result.Increase + calculator.DeliveryTime < problem.Max.Value) { // route will still be inside bounds. best_result = result; best_route_idx = route_idx; } else { // route will become above max. if (result.Increase < best_result_above_max.Increase) { best_result_above_max = result; best_route_above_max_idx = route_idx; } } } } // do the placement if a placement is found without max violation. // else do the placement in the above max route. CheapestInsertionResult placement_result = new CheapestInsertionResult(); placement_result.Increase = double.MaxValue; int placement_result_idx = -1; if (best_route_idx >= 0) { // best placement found. placement_result = best_result; placement_result_idx = best_route_idx; } else { // best placement found but only above max. placement_result = best_result_above_max; placement_result_idx = best_route_above_max_idx; } // do the actual placement. weights[placement_result_idx] = calculator.CalculateOneRouteIncrease( weights[placement_result_idx], placement_result.Increase); selectable_customers.Remove(placement_result.Customer); //routes.Route(placement_result_idx).InsertAfterAndRemove( // placement_result.CustomerBefore, placement_result.Customer, placement_result.CustomerAfter); routes.Route(placement_result_idx).InsertAfter( placement_result.CustomerBefore, placement_result.Customer); if (!routes.IsValid()) { throw new Exception(); } } return(routes); }