#ifndef TRIP_FARTHEST_INSERTION_HPP #define TRIP_FARTHEST_INSERTION_HPP #include "util/dist_table_wrapper.hpp" #include "util/typedefs.hpp" #include "osrm/json_container.hpp" #include #include #include #include #include #include namespace osrm { namespace engine { namespace trip { // given a route and a new location, find the best place of insertion and // check the distance of roundtrip when the new location is additionally visited using NodeIDIter = std::vector::iterator; std::pair GetShortestRoundTrip(const NodeID new_loc, const util::DistTableWrapper &dist_table, const std::size_t number_of_locations, std::vector &route) { (void)number_of_locations; // unused auto min_trip_distance = INVALID_EDGE_WEIGHT; NodeIDIter next_insert_point_candidate; // for all nodes in the current trip find the best insertion resulting in the shortest path // assert min 2 nodes in route const auto start = std::begin(route); const auto end = std::end(route); for (auto from_node = start; from_node != end; ++from_node) { auto to_node = std::next(from_node); if (to_node == end) { to_node = start; } const auto dist_from = dist_table(*from_node, new_loc); const auto dist_to = dist_table(new_loc, *to_node); const auto trip_dist = dist_from + dist_to - dist_table(*from_node, *to_node); BOOST_ASSERT_MSG(dist_from != INVALID_EDGE_WEIGHT, "distance has invalid edge weight"); BOOST_ASSERT_MSG(dist_to != INVALID_EDGE_WEIGHT, "distance has invalid edge weight"); // This is not neccessarily true: // Lets say you have an edge (u, v) with duration 100. If you place a coordinate exactly in // the middle of the segment yielding (u, v'), the adjusted duration will be 100 * 0.5 = 50. // Now imagine two coordinates. One placed at 0.99 and one at 0.999. This means (u, v') now // has a duration of 100 * 0.99 = 99, but (u, v'') also has a duration of 100 * 0.995 = 99. // In which case (v', v'') has a duration of 0. // BOOST_ASSERT_MSG(trip_dist >= 0, "previous trip was not minimal. something's wrong"); // from all possible insertions to the current trip, choose the shortest of all insertions if (trip_dist < min_trip_distance) { min_trip_distance = trip_dist; next_insert_point_candidate = to_node; } } BOOST_ASSERT_MSG(min_trip_distance != INVALID_EDGE_WEIGHT, "trip has invalid edge weight"); return std::make_pair(min_trip_distance, next_insert_point_candidate); } template // given two initial start nodes, find a roundtrip route using the farthest insertion algorithm std::vector FindRoute(const std::size_t &number_of_locations, const std::size_t &component_size, const NodeIDIterator &start, const NodeIDIterator &end, const util::DistTableWrapper &dist_table, const NodeID &start1, const NodeID &start2) { BOOST_ASSERT_MSG(number_of_locations >= component_size, "component size bigger than total number of locations"); std::vector route; route.reserve(number_of_locations); // tracks which nodes have been already visited std::vector visited(number_of_locations, false); visited[start1] = true; visited[start2] = true; route.push_back(start1); route.push_back(start2); // add all other nodes missing (two nodes are already in the initial start trip) for (std::size_t j = 2; j < component_size; ++j) { auto farthest_distance = std::numeric_limits::min(); auto next_node = -1; NodeIDIter next_insert_point; // find unvisited loc i that is the farthest away from all other visited locs for (auto i = start; i != end; ++i) { // find the shortest distance from i to all visited nodes if (!visited[*i]) { const auto insert_candidate = GetShortestRoundTrip(*i, dist_table, number_of_locations, route); BOOST_ASSERT_MSG(insert_candidate.first != INVALID_EDGE_WEIGHT, "shortest round trip is invalid"); // add the location to the current trip such that it results in the shortest total // tour if (insert_candidate.first > farthest_distance) { farthest_distance = insert_candidate.first; next_node = *i; next_insert_point = insert_candidate.second; } } } BOOST_ASSERT_MSG(next_node >= 0, "next node to visit is invalid"); // mark as visited and insert node visited[next_node] = true; route.insert(next_insert_point, next_node); } return route; } template std::vector FarthestInsertionTrip(const NodeIDIterator &start, const NodeIDIterator &end, const std::size_t number_of_locations, const util::DistTableWrapper &dist_table) { ////////////////////////////////////////////////////////////////////////////////////////////////// // 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! ////////////////////////////////////////////////////////////////////////////////////////////////// // Guard against division-by-zero in the code path below. BOOST_ASSERT(number_of_locations > 0); // Guard against dist_table being empty therefore max_element returning the end iterator. BOOST_ASSERT(dist_table.size() > 0); const auto component_size = std::distance(start, end); BOOST_ASSERT(component_size >= 0); auto max_from = -1; auto max_to = -1; if (static_cast(component_size) == number_of_locations) { // find the pair of location with the biggest distance and make the pair the initial start // trip. Skipping over the very first element (0,0), we make sure not to end up with the // same start/end in the special case where all entries are the same. const auto index = std::distance(std::begin(dist_table), std::max_element(std::begin(dist_table) + 1, std::end(dist_table))); max_from = index / number_of_locations; max_to = index % number_of_locations; } else { auto max_dist = std::numeric_limits::min(); for (auto x = start; x != end; ++x) { for (auto y = start; y != end; ++y) { // don't repeat coordinates if (*x == *y) continue; const auto xy_dist = dist_table(*x, *y); // SCC decomposition done correctly? BOOST_ASSERT(xy_dist != INVALID_EDGE_WEIGHT); if (xy_dist >= max_dist) { max_dist = xy_dist; max_from = *x; max_to = *y; } } } } BOOST_ASSERT(max_from >= 0); BOOST_ASSERT(max_to >= 0); BOOST_ASSERT_MSG(static_cast(max_from) < number_of_locations, "start node"); BOOST_ASSERT_MSG(static_cast(max_to) < number_of_locations, "start node"); return FindRoute(number_of_locations, component_size, start, end, dist_table, max_from, max_to); } } } } #endif // TRIP_FARTHEST_INSERTION_HPP