From 80ae041e652130cfdcbeee72c2fd91fbf97e14b7 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Mon, 11 Nov 2024 13:34:35 +0100 Subject: [PATCH] Fix the O(n^3) performance issue in is_connected utility function. Adhere to the specification in comment: // Is the undirected graph connected? // Is the directed graph strongly connected? Instead of checking if each vertex is reachable from each vertex (which is O(n^3)), consider different approaches for directed and undirected graphs: For an undirected graph check if each vertex was reachable with a single DFS walk. This runs in O(N) (modulo back edges). For a directed graph run Tarjan SCC algorithm and check if all vertices end up in a single component. This runs in O(N+E). The speed-up is considerable, e.g. for a 25x25 square connected graph it is about: time: undirected, connected - prev implementation - elapsed 16.458s time: undirected, connected - new implementation - elapsed 6.1249e-05s time: directed, connected - prev implementation - elapsed 7.45684s time: directed, connected - new implementation - elapsed 4.9894e-05s For similar 80x80 graph it's about 0.000563113s, while it just takes forever with the previous O(n^3) approach. --- include/boost/graph/graph_utility.hpp | 57 ++++++++++++++++++++------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp index 4e5ef141a..4be28ef83 100644 --- a/include/boost/graph/graph_utility.hpp +++ b/include/boost/graph/graph_utility.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include // iota moved to detail/algorithm.hpp @@ -339,27 +341,54 @@ inline bool is_reachable( return get(color, y) != color_traits< ColorValue >::white(); } -// Is the undirected graph connected? -// Is the directed graph strongly connected? -template < typename VertexListGraph, typename VertexColorMap > -inline bool is_connected(const VertexListGraph& g, VertexColorMap color) +template < typename Graph, typename VertexColorMap > +inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, undirected_tag) { + // color map should start out white for each vertex typedef typename property_traits< VertexColorMap >::value_type ColorValue; typedef color_traits< ColorValue > Color; - typename graph_traits< VertexListGraph >::vertex_iterator ui, ui_end, vi, - vi_end, ci, ci_end; + + default_dfs_visitor vis; + detail::depth_first_visit_impl(g, detail::get_default_starting_vertex(g), + vis, color, detail::nontruth2()); + + // If an undirected graph is connected, then each vertex is reachable in a + // single DFS visit. If any vertex was unreachable, grpah is not connected. + typename graph_traits< Graph >::vertex_iterator ui, ui_end; for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - if (*ui != *vi) - { - for (boost::tie(ci, ci_end) = vertices(g); ci != ci_end; ++ci) - put(color, *ci, Color::white()); - if (!is_reachable(*ui, *vi, g, color)) - return false; - } + if (get(color, *ui) == Color::white()) + return false; + return true; + +} + +template < typename Graph, typename VertexColorMap > +inline bool is_connected_dispatch(const Graph& g, VertexColorMap, directed_tag) +{ + // Run the Tarjan SCC algorithm + std::vector< size_t > comp_map(num_vertices(g)); + strong_components(g, + make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); + + // If the directed graph is strongly connected, all vertices are in + // the same component 0 + for (std::vector< size_t >::const_iterator i = comp_map.begin(); + i != comp_map.end(); ++i) + if (*i > 0) + return false; return true; } + +// Is the undirected graph connected? +// Is the directed graph strongly connected? +template < typename VertexListGraph, typename VertexColorMap > +inline bool is_connected(const VertexListGraph& g, VertexColorMap color) +{ + typedef typename graph_traits< VertexListGraph >::directed_category Cat; + return is_connected_dispatch(g, color, Cat()); +} + template < typename Graph > bool is_self_loop( typename graph_traits< Graph >::edge_descriptor e, const Graph& g)