diff --git a/plugins/round_trip.hpp b/plugins/round_trip.hpp index 3b984623b..e084007f1 100644 --- a/plugins/round_trip.hpp +++ b/plugins/round_trip.hpp @@ -59,6 +59,122 @@ template class RoundTripPlugin final : public BasePlugin DataFacadeT *facade; std::unique_ptr> search_engine_ptr; + void NearestNeighbour(const RouteParameters & route_parameters, + const PhantomNodeArray & phantom_node_vector, + std::vector & result_table, + InternalRouteResult & min_route, + std::vector & min_loc_permutation) { + ////////////////////////////////////////////////////////////////////////////////////////////////// + // START GREEDY NEAREST NEIGHBOUR HERE + // 1. grab a random location and mark as starting point + // 2. find the nearest unvisited neighbour, set it as the current location and mark as visited + // 3. repeat 2 until there is no unvisited location + // 4. return route back to starting point + // 5. compute route + // 6. repeat 1-5 with different starting points and choose iteration with shortest trip + // 6. DONE! + ////////////////////////////////////////////////////////////////////////////////////////////////// + + const auto number_of_locations = phantom_node_vector.size(); + min_route.shortest_path_length = std::numeric_limits::max(); + + // is_lonely_island[i] indicates whether node i is a node that cannot be reached from other nodes + // 1 means that node i is a lonely island + // 0 means that it is not known for node i + // -1 means that node i is not a lonely island but a reachable, connected node + std::vector is_lonely_island(number_of_locations, 0); + int count_unreachables; + + // ALWAYS START AT ANOTHER STARTING POINT + for(int start_node = 0; start_node < number_of_locations; ++start_node) + { + + if (is_lonely_island[start_node] >= 0) + { + // if node is a lonely island it is an unsuitable node to start from and shall be skipped + if (is_lonely_island[start_node]) + continue; + count_unreachables = 0; + auto start_dist_begin = result_table.begin() + (start_node * number_of_locations); + auto start_dist_end = result_table.begin() + ((start_node + 1) * number_of_locations); + for (auto it2 = start_dist_begin; it2 != start_dist_end; ++it2) { + if (*it2 == 0 || *it2 == std::numeric_limits::max()) { + ++count_unreachables; + } + } + if (count_unreachables >= number_of_locations) { + is_lonely_island[start_node] = 1; + continue; + } + } + + int curr_node = start_node; + is_lonely_island[curr_node] = -1; + InternalRouteResult raw_route; + //TODO: Should we always use the same vector or does it not matter at all because of loop scope? + std::vector loc_permutation(number_of_locations, -1); + loc_permutation[start_node] = 0; + // visited[i] indicates whether node i was already visited by the salesman + std::vector visited(number_of_locations, false); + visited[start_node] = true; + + PhantomNodes viapoint; + // 3. REPEAT FOR EVERY UNVISITED NODE + for(int via_point = 1; via_point < number_of_locations; ++via_point) + { + int min_dist = std::numeric_limits::max(); + int min_id = -1; + + // 2. FIND NEAREST NEIGHBOUR + auto row_begin_iterator = result_table.begin() + (curr_node * number_of_locations); + auto row_end_iterator = result_table.begin() + ((curr_node + 1) * number_of_locations); + for (auto it = row_begin_iterator; it != row_end_iterator; ++it) { + auto index = std::distance(row_begin_iterator, it); + if (is_lonely_island[index] < 1 && !visited[index] && *it < min_dist) + { + min_dist = *it; + min_id = index; + } + } + // in case there was no unvisited and reachable node found, it means that all remaining (unvisited) nodes must be lonely islands + if (min_id == -1) + { + for(int loc = 0; loc < visited.size(); ++loc) { + if (!visited[loc]) { + is_lonely_island[loc] = 1; + } + } + break; + } + // set the nearest unvisited location as the next via_point + else + { + is_lonely_island[min_id] = -1; + loc_permutation[min_id] = via_point; + visited[min_id] = true; + viapoint = PhantomNodes{phantom_node_vector[curr_node][0], phantom_node_vector[min_id][0]}; + raw_route.segment_end_coordinates.emplace_back(viapoint); + curr_node = min_id; + } + } + + // 4. ROUTE BACK TO STARTING POINT + viapoint = PhantomNodes{raw_route.segment_end_coordinates.back().target_phantom, phantom_node_vector[start_node][0]}; + raw_route.segment_end_coordinates.emplace_back(viapoint); + + // 5. COMPUTE ROUTE + search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, route_parameters.uturns, raw_route); + + // check round trip with this starting point is shorter than the shortest round trip found till now + if (raw_route.shortest_path_length < min_route.shortest_path_length) { + min_route = raw_route; + min_loc_permutation = loc_permutation; + } + } + + SimpleLogger().Write() << "Shortest route " << min_route.shortest_path_length; + } + public: explicit RoundTripPlugin(DataFacadeT *facade) : descriptor_string("trip"), facade(facade) @@ -104,7 +220,7 @@ template class RoundTripPlugin final : public BasePlugin } // compute the distance table of all phantom nodes - std::shared_ptr> result_table = + const std::shared_ptr> result_table = search_engine_ptr->distance_table(phantom_node_vector); if (!result_table) @@ -112,120 +228,10 @@ template class RoundTripPlugin final : public BasePlugin return 400; } - ////////////////////////////////////////////////////////////////////////////////////////////////// - // START GREEDY NEAREST NEIGHBOUR HERE - // 1. grab a random location and mark as starting point - // 2. find the nearest unvisited neighbour, set it as the current location and mark as visited - // 3. repeat 2 until there is no unvisited location - // 4. return route back to starting point - // 5. compute route - // 6. repeat 1-5 with different starting points and choose iteration with shortest trip - // 6. DONE! - ////////////////////////////////////////////////////////////////////////////////////////////////// - - const auto number_of_locations = phantom_node_vector.size(); - // min_route is the shortest route found + // compute TSP round trip InternalRouteResult min_route; - min_route.shortest_path_length = std::numeric_limits::max(); - // min_loc_permutation stores the order of visited locations of the shortest route std::vector min_loc_permutation; - - // is_lonely_island[i] indicates whether node i is a node that cannot be reached from other nodes - // 1 means that node i is a lonely island - // 0 means that it is not known for node i - // -1 means that node i is not a lonely island but a reachable, connected node - std::vector is_lonely_island(number_of_locations, 0); - int count_unreachables; - - // ALWAYS START AT ANOTHER STARTING POINT - for(int start_node = 0; start_node < number_of_locations; ++start_node) - { - - if (is_lonely_island[start_node] >= 0) - { - // if node is a lonely island it is an unsuitable node to start from and shall be skipped - if (is_lonely_island[start_node]) - continue; - count_unreachables = 0; - auto start_dist_begin = result_table->begin() + (start_node * number_of_locations); - auto start_dist_end = result_table->begin() + ((start_node + 1) * number_of_locations); - for (auto it2 = start_dist_begin; it2 != start_dist_end; ++it2) { - if (*it2 == 0 || *it2 == std::numeric_limits::max()) { - ++count_unreachables; - } - } - if (count_unreachables >= number_of_locations) { - is_lonely_island[start_node] = 1; - continue; - } - } - - int curr_node = start_node; - is_lonely_island[curr_node] = -1; - InternalRouteResult raw_route; - //TODO: Should we always use the same vector or does it not matter at all because of loop scope? - std::vector loc_permutation(number_of_locations, -1); - loc_permutation[start_node] = 0; - // visited[i] indicates whether node i was already visited by the salesman - std::vector visited(number_of_locations, false); - visited[start_node] = true; - - PhantomNodes viapoint; - // 3. REPEAT FOR EVERY UNVISITED NODE - for(int via_point = 1; via_point < number_of_locations; ++via_point) - { - int min_dist = std::numeric_limits::max(); - int min_id = -1; - - // 2. FIND NEAREST NEIGHBOUR - auto row_begin_iterator = result_table->begin() + (curr_node * number_of_locations); - auto row_end_iterator = result_table->begin() + ((curr_node + 1) * number_of_locations); - for (auto it = row_begin_iterator; it != row_end_iterator; ++it) { - auto index = std::distance(row_begin_iterator, it); - if (is_lonely_island[index] < 1 && !visited[index] && *it < min_dist) - { - min_dist = *it; - min_id = index; - } - } - // in case there was no unvisited and reachable node found, it means that all remaining (unvisited) nodes must be lonely islands - if (min_id == -1) - { - for(int loc = 0; loc < visited.size(); ++loc) { - if (!visited[loc]) { - is_lonely_island[loc] = 1; - } - } - break; - } - // set the nearest unvisited location as the next via_point - else - { - is_lonely_island[min_id] = -1; - loc_permutation[min_id] = via_point; - visited[min_id] = true; - viapoint = PhantomNodes{phantom_node_vector[curr_node][0], phantom_node_vector[min_id][0]}; - raw_route.segment_end_coordinates.emplace_back(viapoint); - curr_node = min_id; - } - } - - // 4. ROUTE BACK TO STARTING POINT - viapoint = PhantomNodes{raw_route.segment_end_coordinates.back().target_phantom, phantom_node_vector[start_node][0]}; - raw_route.segment_end_coordinates.emplace_back(viapoint); - - // 5. COMPUTE ROUTE - search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, route_parameters.uturns, raw_route); - // SimpleLogger().Write() << "Route starting at " << start_node << " with length " << raw_route.shortest_path_length; - - // check round trip with this starting point is shorter than the shortest round trip found till now - if (raw_route.shortest_path_length < min_route.shortest_path_length) { - min_route = raw_route; - min_loc_permutation = loc_permutation; - } - } - - SimpleLogger().Write() << "Shortest route " << min_route.shortest_path_length; + NearestNeighbour(route_parameters, phantom_node_vector, *result_table, min_route, min_loc_permutation); // return result to json std::unique_ptr> descriptor;