diff --git a/plugins/round_trip.hpp b/plugins/round_trip.hpp index 85d1f2057..652e82f3b 100644 --- a/plugins/round_trip.hpp +++ b/plugins/round_trip.hpp @@ -31,6 +31,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "plugin_base.hpp" #include "../algorithms/object_encoder.hpp" +#include "../routing_algorithms/tsp_nearest_neighbour.hpp" +#include "../routing_algorithms/tsp_farthest_insertion.hpp" +#include "../routing_algorithms/tsp_brute_force.hpp" #include "../data_structures/query_edge.hpp" #include "../data_structures/search_engine.hpp" #include "../descriptors/descriptor_base.hpp" @@ -44,7 +47,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include - #include #include #include @@ -59,223 +61,6 @@ template class RoundTripPlugin final : public BasePlugin DataFacadeT *facade; std::unique_ptr> search_engine_ptr; - void FarthestInsertion(const RouteParameters & route_parameters, - const PhantomNodeArray & phantom_node_vector, - const std::vector & dist_table, - InternalRouteResult & min_route, - std::vector & min_loc_permutation) { - ////////////////////////////////////////////////////////////////////////////////////////////////// - // START FARTHEST INSERTION HERE - // 1. start at a random round trip of 2 locations - // 2. find the location that is the farthest away from the visited locations - // 3. add the found location to the current round trip such that round trip is the shortest - // 4. repeat 2-3 until all locations are visited - // 5. DONE! - ////////////////////////////////////////////////////////////////////////////////////////////////// - - const auto number_of_locations = phantom_node_vector.size(); - std::list current_trip; - std::vector visited(number_of_locations, false); - - // find two locations that have max distance - auto max_dist = -1; - int max_from = -1; - int max_to = -1; - - auto i = 0; - for (auto it = dist_table.begin(); it != dist_table.end(); ++it) { - if (*it > max_dist) { - max_dist = *it; - max_from = i / number_of_locations; - max_to = i % number_of_locations; - } - ++i; - } - - visited[max_from] = true; - visited[max_to] = true; - - // SimpleLogger().Write() << "Start with " << max_from << " " << max_to; - - current_trip.push_back(max_from); - current_trip.push_back(max_to); - - for (int j = 2; j < number_of_locations; ++j) { - auto max_min_dist = -1; - int next_node = -1; - auto min_max_insert = current_trip.begin(); - - // look for loc i that is the farthest away from all other visited locs - for (int i = 0; i < number_of_locations; ++i) { - if (!visited[i]) { - // SimpleLogger().Write() << "- node " << i << " is not visited yet"; - - auto min_insert = std::numeric_limits::max(); - auto min_to = current_trip.begin(); - - for (auto from_node = current_trip.begin(); from_node != current_trip.end(); ++from_node) { - - auto to_node = std::next(from_node); - if (std::next(from_node) == current_trip.end()) { - to_node = current_trip.begin(); - } - - auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + i); - auto dist_to = *(dist_table.begin() + (i * number_of_locations) + *to_node); - auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); - - // SimpleLogger().Write() << " From " << *from_node << " to " << i << " to " << *to_node << " is " << trip_dist; - - if (trip_dist < min_insert) { - min_insert = trip_dist; - min_to = to_node; - } - } - if (min_insert > max_min_dist) { - max_min_dist = min_insert; - next_node = i; - min_max_insert = min_to; - } - } - } - // SimpleLogger().Write() << "- Insert new node " << next_node; - visited[next_node] = true; - - current_trip.insert(min_max_insert, next_node); - } - - int perm = 0; - for (auto it = current_trip.begin(); it != current_trip.end(); ++it) { - // SimpleLogger().Write() << "- Visit location " << *it; - - auto from_node = *it; - auto to_node = *std::next(it); - if (std::next(it) == current_trip.end()) { - to_node = current_trip.front(); - } - PhantomNodes viapoint; - viapoint = PhantomNodes{phantom_node_vector[from_node][0], phantom_node_vector[to_node][0]}; - min_route.segment_end_coordinates.emplace_back(viapoint); - min_loc_permutation[from_node] = perm; - ++perm; - } - search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - } - - void NearestNeighbour(const RouteParameters & route_parameters, - const PhantomNodeArray & phantom_node_vector, - const std::vector & dist_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 - // 7. 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 = dist_table.begin() + (start_node * number_of_locations); - auto start_dist_end = dist_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 = dist_table.begin() + (curr_node * number_of_locations); - auto row_end_iterator = dist_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; - } - } - } - public: explicit RoundTripPlugin(DataFacadeT *facade) : descriptor_string("trip"), facade(facade) @@ -333,50 +118,70 @@ template class RoundTripPlugin final : public BasePlugin // compute TSP round trip InternalRouteResult min_route_nn; InternalRouteResult min_route_fi; + InternalRouteResult min_route_bf; std::vector min_loc_permutation_nn(phantom_node_vector.size(), -1); std::vector min_loc_permutation_fi(phantom_node_vector.size(), -1); + std::vector min_loc_permutation_bf(phantom_node_vector.size(), -1); TIMER_STOP(tsp_pre); + + //######################### NEAREST NEIGHBOUR ###############################// TIMER_START(tsp_nn); - NearestNeighbour(route_parameters, phantom_node_vector, *result_table, min_route_nn, min_loc_permutation_nn); + osrm::tsp::NearestNeighbour(route_parameters, phantom_node_vector, *result_table, min_route_nn, min_loc_permutation_nn); + search_engine_ptr->shortest_path(min_route_nn.segment_end_coordinates, route_parameters.uturns, min_route_nn); TIMER_STOP(tsp_nn); SimpleLogger().Write() << "Distance " << min_route_nn.shortest_path_length; SimpleLogger().Write() << "Time " << TIMER_MSEC(tsp_nn) + TIMER_MSEC(tsp_pre); - // std::unique_ptr> descriptor; - // descriptor = osrm::make_unique>(facade); - - // descriptor->SetConfig(route_parameters); - // descriptor->Run(min_route_nn, json_result); - osrm::json::Array json_loc_permutation_nn; json_loc_permutation_nn.values.insert(json_loc_permutation_nn.values.end(), min_loc_permutation_nn.begin(), min_loc_permutation_nn.end()); json_result.values["nn_loc_permutation"] = json_loc_permutation_nn; json_result.values["nn_distance"] = min_route_nn.shortest_path_length; json_result.values["nn_runtime"] = TIMER_MSEC(tsp_nn) + TIMER_MSEC(tsp_pre); + + //########################### BRUTE FORCE ####################################// + if (route_parameters.coordinates.size() < 12) { + TIMER_START(tsp_bf); + osrm::tsp::BruteForce(route_parameters, phantom_node_vector, *result_table, min_route_bf, min_loc_permutation_bf); + search_engine_ptr->shortest_path(min_route_bf.segment_end_coordinates, route_parameters.uturns, min_route_bf); + TIMER_STOP(tsp_bf); + SimpleLogger().Write() << "Distance " << min_route_bf.shortest_path_length; + SimpleLogger().Write() << "Time " << TIMER_MSEC(tsp_bf) + TIMER_MSEC(tsp_pre); + + osrm::json::Array json_loc_permutation_bf; + json_loc_permutation_bf.values.insert(json_loc_permutation_bf.values.end(), min_loc_permutation_bf.begin(), min_loc_permutation_bf.end()); + json_result.values["bf_loc_permutation"] = json_loc_permutation_bf; + json_result.values["bf_distance"] = min_route_bf.shortest_path_length; + json_result.values["bf_runtime"] = TIMER_MSEC(tsp_bf) + TIMER_MSEC(tsp_pre); + } else { + json_result.values["bf_distance"] = -1; + json_result.values["bf_runtime"] = -1; + } + + + //######################## FARTHEST INSERTION ###############################// TIMER_START(tsp_fi); - FarthestInsertion(route_parameters, phantom_node_vector, *result_table, min_route_fi, min_loc_permutation_fi); + osrm::tsp::FarthestInsertion(route_parameters, phantom_node_vector, *result_table, min_route_fi, min_loc_permutation_fi); + search_engine_ptr->shortest_path(min_route_fi.segment_end_coordinates, route_parameters.uturns, min_route_fi); TIMER_STOP(tsp_fi); SimpleLogger().Write() << "Distance " << min_route_fi.shortest_path_length; SimpleLogger().Write() << "Time " << TIMER_MSEC(tsp_fi) + TIMER_MSEC(tsp_pre); - // return result to json - std::unique_ptr> descriptor; - descriptor = osrm::make_unique>(facade); - - descriptor->SetConfig(route_parameters); - descriptor->Run(min_route_fi, json_result); - osrm::json::Array json_loc_permutation_fi; json_loc_permutation_fi.values.insert(json_loc_permutation_fi.values.end(), min_loc_permutation_fi.begin(), min_loc_permutation_fi.end()); json_result.values["fi_loc_permutation"] = json_loc_permutation_fi; json_result.values["fi_distance"] = min_route_fi.shortest_path_length; json_result.values["fi_runtime"] = TIMER_MSEC(tsp_fi) + TIMER_MSEC(tsp_pre); - // for (int i = 0; i < min_loc_permutation_fi.size(); ++i) { - // SimpleLogger().Write() << min_loc_permutation_nn[i] << " " << min_loc_permutation_fi[i]; - // } + // return geometry result to json + std::unique_ptr> descriptor; + descriptor = osrm::make_unique>(facade); + + descriptor->SetConfig(route_parameters); + descriptor->Run(min_route_fi, json_result); + + return 200; } diff --git a/routing_algorithms/tsp_farthest_insertion.hpp b/routing_algorithms/tsp_farthest_insertion.hpp new file mode 100644 index 000000000..0f5c309b3 --- /dev/null +++ b/routing_algorithms/tsp_farthest_insertion.hpp @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TSP_FARTHEST_INSERTION_HPP +#define TSP_FARTHEST_INSERTION_HPP + + +#include "../data_structures/search_engine.hpp" +#include "../util/string_util.hpp" + +#include + +#include + +#include +#include +#include +#include + + +namespace osrm +{ +namespace tsp +{ + +void FarthestInsertion(const RouteParameters & route_parameters, + const PhantomNodeArray & phantom_node_vector, + const std::vector & dist_table, + InternalRouteResult & min_route, + std::vector & min_loc_permutation) { + ////////////////////////////////////////////////////////////////////////////////////////////////// + // START FARTHEST INSERTION HERE + // 1. start at a random round trip of 2 locations + // 2. find the location that is the farthest away from the visited locations and whose insertion will make the round trip the longest + // 3. add the found location to the current round trip such that round trip is the shortest + // 4. repeat 2-3 until all locations are visited + // 5. DONE! + ////////////////////////////////////////////////////////////////////////////////////////////////// + + const auto number_of_locations = phantom_node_vector.size(); + // list of the trip that will be found incrementally + std::list current_trip; + // tracks which nodes have been already visited + std::vector visited(number_of_locations, false); + + + // find the pair of location with the biggest distance and make the pair the initial start trip + const auto index = std::distance(dist_table.begin(), std::max_element(dist_table.begin(), dist_table.end())); + const int max_from = index / number_of_locations; + const int max_to = index % number_of_locations; + + visited[max_from] = true; + visited[max_to] = true; + current_trip.push_back(max_from); + current_trip.push_back(max_to); + + // add all other nodes missing (two nodes are already in the initial start trip) + for (int j = 2; j < number_of_locations; ++j) { + auto shortest_max_tour = -1; + int next_node = -1; + std::list::iterator min_max_insert; + + // find unvisited loc i that is the farthest away from all other visited locs + for (int i = 0; i < number_of_locations; ++i) { + if (!visited[i]) { + // longest_min_tour is the distance of the longest of all insertions with the minimal distance + auto longest_min_tour = std::numeric_limits::max(); + // following_loc is the location that comes after the location that is to be inserted + std::list::iterator following_loc; + + // for all nodes in the current trip find the best insertion resulting in the shortest path + for (auto from_node = current_trip.begin(); from_node != std::prev(current_trip.end()); ++from_node) { + auto to_node = std::next(from_node); + + auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + i); + auto dist_to = *(dist_table.begin() + (i * number_of_locations) + *to_node); + auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); + + // from all possible insertions to the current trip, choose the longest of all minimal insertions + if (trip_dist < longest_min_tour) { + longest_min_tour = trip_dist; + following_loc = to_node; + } + } + { // check insertion between last and first location too + auto from_node = std::prev(current_trip.end()); + auto to_node = current_trip.begin(); + + auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + i); + auto dist_to = *(dist_table.begin() + (i * number_of_locations) + *to_node); + auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); + if (trip_dist < longest_min_tour) { + longest_min_tour = trip_dist; + following_loc = to_node; + } + } + + // add the location to the current trip such that it results in the shortest total tour + if (longest_min_tour > shortest_max_tour) { + shortest_max_tour = longest_min_tour; + next_node = i; + min_max_insert = following_loc; + } + } + } + // mark as visited and insert node + visited[next_node] = true; + current_trip.insert(min_max_insert, next_node); + } + + // given he final trip, compute total distance and return the route and location permutation + PhantomNodes viapoint; + int perm = 0; + for (auto it = current_trip.begin(); it != std::prev(current_trip.end()); ++it) { + auto from_node = *it; + auto to_node = *std::next(it); + + viapoint = PhantomNodes{phantom_node_vector[from_node][0], phantom_node_vector[to_node][0]}; + min_route.segment_end_coordinates.emplace_back(viapoint); + + min_loc_permutation[from_node] = perm; + ++perm; + } + { // check dist between last and first location too + viapoint = PhantomNodes{phantom_node_vector[*std::prev(current_trip.end())][0], phantom_node_vector[current_trip.front()][0]}; + min_route.segment_end_coordinates.emplace_back(viapoint); + min_loc_permutation[*std::prev(current_trip.end())] = perm; + } +} + + +} +} + +#endif // TSP_FARTHEST_INSERTION_HPP \ No newline at end of file diff --git a/routing_algorithms/tsp_nearest_neighbour.hpp b/routing_algorithms/tsp_nearest_neighbour.hpp new file mode 100644 index 000000000..11ff7b905 --- /dev/null +++ b/routing_algorithms/tsp_nearest_neighbour.hpp @@ -0,0 +1,168 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TSP_NEAREST_NEIGHBOUR_HPP +#define TSP_NEAREST_NEIGHBOUR_HPP + + +#include "../data_structures/search_engine.hpp" +#include "../util/string_util.hpp" +#include "../util/simple_logger.hpp" + +#include + +#include +#include +#include +#include +#include + + + +namespace osrm +{ +namespace tsp +{ + +void NearestNeighbour(const RouteParameters & route_parameters, + const PhantomNodeArray & phantom_node_vector, + const std::vector & dist_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 + // 7. 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 = dist_table.begin() + (start_node * number_of_locations); + auto start_dist_end = dist_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 + int trip_dist = 0; + 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 = dist_table.begin() + (curr_node * number_of_locations); + auto row_end_iterator = dist_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); + trip_dist += min_dist; + 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); + + // check round trip with this starting point is shorter than the shortest round trip found till now + if (trip_dist < min_route.shortest_path_length) { + min_route = raw_route; + min_route.shortest_path_length = trip_dist; + //TODO: this gets copied right? fix this + min_loc_permutation = loc_permutation; + } + } +} + +} +} +#endif // TSP_NEAREST_NEIGHBOUR_HPP \ No newline at end of file