diff --git a/src/graph/graph.h b/src/graph/graph.h index 651866b499..edd2f15418 100644 --- a/src/graph/graph.h +++ b/src/graph/graph.h @@ -1029,8 +1029,14 @@ void BaseGraph:: t##ArcIterator(*this, node, e)); \ } -// Adapt our old iteration style to support range-based for loops. +// Adapt our old iteration style to support range-based for loops. Add typedefs +// required by std::iterator_traits. #define DEFINE_STL_ITERATOR_FUNCTIONS(iterator_class_name) \ + using iterator_category = std::input_iterator_tag; \ + using difference_type = ptrdiff_t; \ + using pointer = const ArcIndexType*; \ + using reference = const ArcIndexType&; \ + using value_type = ArcIndexType; \ bool operator!=(const iterator_class_name& other) const { \ return index_ != other.index_; \ } \ @@ -1152,6 +1158,12 @@ class ListGraph::OutgoingArcIterator { template class ListGraph::OutgoingHeadIterator { public: + using iterator_category = std::input_iterator_tag; + using difference_type = ptrdiff_t; + using pointer = const NodeIndexType*; + using reference = const NodeIndexType&; + using value_type = NodeIndexType; + OutgoingHeadIterator(const ListGraph& graph, NodeIndexType node) : graph_(graph), index_(graph.start_[node]) { DCHECK(graph.IsNodeValid(node)); @@ -1583,13 +1595,8 @@ class ReverseArcListGraph::OutgoingHeadIterator { DCHECK(Ok()); index_ = graph_->next_[index_]; } - bool operator!=( - const typename ReverseArcListGraph< - NodeIndexType, ArcIndexType>::OutgoingHeadIterator& other) const { - return index_ != other.index_; - } - ArcIndexType operator*() const { return Index(); } - void operator++() { Next(); } + + DEFINE_STL_ITERATOR_FUNCTIONS(OutgoingHeadIterator); private: const ReverseArcListGraph* graph_; @@ -2156,6 +2163,23 @@ class CompleteBipartiteGraph ArcIndexType from) const; IntegerRange operator[](NodeIndexType node) const; + // Deprecated interface. + class OutgoingArcIterator { + public: + OutgoingArcIterator(const CompleteBipartiteGraph& graph, NodeIndexType node) + : index_(graph.right_nodes_ * node), + limit_(node >= graph.left_nodes_ ? index_ + : graph.right_nodes_ * (node + 1)) {} + + bool Ok() const { return index_ < limit_; } + ArcIndexType Index() const { return index_; } + void Next() { index_++; } + + private: + ArcIndexType index_; + const ArcIndexType limit_; + }; + private: const NodeIndexType left_nodes_; const NodeIndexType right_nodes_; @@ -2165,7 +2189,7 @@ template NodeIndexType CompleteBipartiteGraph::Head( ArcIndexType arc) const { DCHECK(this->IsArcValid(arc)); - return arc % right_nodes_; + return left_nodes_ + arc % right_nodes_; } template @@ -2207,7 +2231,7 @@ template IntegerRange CompleteBipartiteGraph< NodeIndexType, ArcIndexType>::operator[](NodeIndexType node) const { if (node < left_nodes_) { - return IntegerRange(0, right_nodes_); + return IntegerRange(left_nodes_, left_nodes_ + right_nodes_); } else { return IntegerRange(0, 0); } diff --git a/src/graph/hamiltonian_path.h b/src/graph/hamiltonian_path.h index 8f564431b0..2e7b86e881 100644 --- a/src/graph/hamiltonian_path.h +++ b/src/graph/hamiltonian_path.h @@ -27,15 +27,14 @@ // anywhere, but you have to return to your start location. // // By complete we mean that the algorithm guarantees to compute the optimal -// solution. -// The algorithm uses dynamic programming. Its time complexity is -// O(n * 2 ^ (n - 1)), where n is the number of nodes to be visited, and '^' -// denotes exponentiation. Its space complexity is also O(n * 2 ^ (n - 1)). +// solution. The algorithm uses dynamic programming. Its time complexity is +// O(n^2 * 2^(n-1)), where n is the number of nodes to be visited, and '^' +// denotes exponentiation. Its space complexity is O(n * 2 ^ (n - 1)). // // Note that the naive implementation of the SHPP // exploring all permutations without memorizing intermediate results would // have a complexity of (n - 1)! (factorial of (n - 1) ), which is much higher -// than n * 2 ^ (n - 1). To convince oneself of this, just use Stirling's +// than n^2 * 2^(n-1). To convince oneself of this, just use Stirling's // formula: n! ~ sqrt(2 * pi * n)*( n / exp(1)) ^ n. // Because of these complexity figures, the algorithm is not practical for // problems with more than 20 nodes. diff --git a/src/graph/util.h b/src/graph/util.h index 730c97ed17..67f11570b1 100644 --- a/src/graph/util.h +++ b/src/graph/util.h @@ -22,6 +22,7 @@ #include #include "base/numbers.h" +#include "base/hash.h" #include "base/split.h" #include "base/join.h" #include "base/murmur.h" @@ -34,8 +35,7 @@ namespace operations_research { // Diagnoses whether a graph is symmetric. A graph is symmetric iff // for all (a, b), the number of arcs a->b is equal to the number of arcs b->a. -// If it returns "false", the graph is certainly not symmetric; if it returns -// "true" then the graph is most likely symmetric. It works in O(graph size). +// Works in O(graph size). template bool GraphIsSymmetric(const Graph& graph); @@ -47,6 +47,16 @@ template util::StatusOr RemapGraph(const Graph& graph, const std::vector& new_node_index); +// Returns a std::string representation of a graph: one arc per line. Eg.: +// "1->2\n3->3" for a graph with 4 nodes and 2 arcs (1->2) and (3->3). +// Arcs are sorted by their tail, then by the order of OutgoingArcs(). +template +std::string GraphToString(const Graph& graph); + +// Returns a copy of "graph", without self-arcs and duplicate arcs. +template +std::unique_ptr RemoveSelfArcsAndDuplicateArcs(const Graph& graph); + // Read a graph file in the simple ".g" format: the file should be a text file // containing only space-separated integers, whose first line is: // [ @@ -166,6 +176,36 @@ util::StatusOr RemapGraph(const Graph& old_graph, return new_graph.release(); } +template +std::string GraphToString(const Graph& graph) { + std::string out; + for (const typename Graph::NodeIndex node : graph.AllNodes()) { + for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) { + if (!out.empty()) out += '\n'; + StrAppend(&out, node, "->", graph.Head(arc)); + } + } + return out; +} + +template +std::unique_ptr RemoveSelfArcsAndDuplicateArcs(const Graph& graph) { + std::unique_ptr g(new Graph(graph.num_nodes(), graph.num_arcs())); + typedef typename Graph::ArcIndex ArcIndex; + typedef typename Graph::NodeIndex NodeIndex; + hash_set> arcs; + for (const NodeIndex tail : graph.AllNodes()) { + for (const ArcIndex arc : graph.OutgoingArcs(tail)) { + const NodeIndex head = graph.Head(arc); + if (head != tail && arcs.insert({tail, head}).second) { + g->AddArc(tail, head); + } + } + } + g->Build(); + return g; +} + template util::StatusOr ReadGraphFile( const std::string& filename, bool directed,