diff --git a/Makefile b/Makefile index 32eb08635b..e48f3151d2 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,13 @@ BINARIES=nqueens golomb magic_square cryptarithm all: libs $(BINARIES) pylib -libs: libcp.a libutil.a libbase.a libalgorithms.a libgraph.a +libs: \ + libcp.a \ + libutil.a \ + libbase.a \ + libalgorithms.a \ + libgraph.a \ + libshortestpaths.a clean: rm -f *.a @@ -149,6 +155,21 @@ objs/bron_kerbosch.o:graph/bron_kerbosch.cc libgraph.a: $(GRAPH_LIB_OBJS) ar rv libgraph.a $(GRAPH_LIB_OBJS) +# Shortestpaths library. + +SHORTESTPATHS_LIB_OBJS=\ + objs/bellman_ford.o \ + objs/dijkstra.o + +objs/bellman_ford.o:graph/bellman_ford.cc + $(CCC) $(CFLAGS) -c graph/bellman_ford.cc -o objs/bellman_ford.o + +objs/dijkstra.o:graph/dijkstra.cc + $(CCC) $(CFLAGS) -c graph/dijkstra.cc -o objs/dijkstra.o + +libshortestpaths.a: $(SHORTESTPATHS_LIB_OBJS) + ar rv libshortestpaths.a $(SHORTESTPATHS_LIB_OBJS) + # Algorithms library. ALGORITHMS_LIB_OBJS=\ diff --git a/Makefile.msv b/Makefile.msv index 9a384117e5..cfac629f61 100644 --- a/Makefile.msv +++ b/Makefile.msv @@ -24,7 +24,14 @@ BINARIES=nqueens.exe golomb.exe magic_square.exe cryptarithm.exe all: libs $(BINARIES) -LIBS = cp.lib util.lib base.lib gflags.lib algorithms.lib graph.lib +LIBS = \ + cp.lib \ + util.lib \ + base.lib \ + gflags.lib \ + algorithms.lib \ + graph.lib \ + shortestpaths.lib libs: $(LIBS) @@ -135,6 +142,21 @@ objs/bron_kerbosch.obj:graph/bron_kerbosch.cc graph.lib: $(GRAPH_LIB_OBJS) lib /OUT:graph.lib $(GRAPH_LIB_OBJS) +# Shortestpaths library. + +SHORTESTPATHS_LIB_OBJS=\ + objs/bellman_ford.obj \ + objs/dijkstra.obj + +objs/bellman_ford.o:graph/bellman_ford.cc + $(CCC) $(CFLAGS) -c graph/bellman_ford.cc /Foobjs/bellman_ford.obj + +objs/dijkstra.o:graph/dijkstra.cc + $(CCC) $(CFLAGS) -c graph/dijkstra.cc /Foobjs/dijkstra.obj + +libshortestpaths.a: $(SHORTESTPATHS_LIB_OBJS) + lib /OUT:shortestpaths.lib $(SHORTESTPATHS_LIB_OBJS) + # Algorithms library. ALGORITHMS_LIB_OBJS=\ diff --git a/base/adjustable_priority_queue.h b/base/adjustable_priority_queue.h new file mode 100644 index 0000000000..c7d8b143ef --- /dev/null +++ b/base/adjustable_priority_queue.h @@ -0,0 +1,134 @@ +// Copyright 2010 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/macros.h" + +#ifndef BASE_ADJUSTABLE_PRIORITY_QUEUE_H +#define BASE_ADJUSTABLE_PRIORITY_QUEUE_H + +namespace operations_research { + +template class AdjustablePriorityQueue { + public: + + AdjustablePriorityQueue() {} + + void Add(T* const val) { + elems_.push_back(val); + AdjustUpwards(elems_.size() - 1); + } + + void Remove(T* const val) { + int i = val->GetHeapIndex(); + if (i == Size() - 1) { + elems_.resize(Size()-1); + return; + } + elems_[i] = elems_.back(); + elems_[i]->SetHeapIndex(i); + elems_.pop_back(); + NoteChangedPriority(elems_[i]); + } + + bool Contains(const T* const val) const { + const int i = val->GetHeapIndex(); + if (i < 0 || i >= elems_.size() || elems_[i] != val) { + return false; + } + return true; + } + + void NoteChangedPriority(T* val) { + const int i = val->GetHeapIndex(); + const int parent = (i - 1) / 2; + if (*elems_[parent] < *val) { + AdjustUpwards(i); + } else { + AdjustDownwards(i); + } + } + + T* Top() { return elems_[0]; } + + const T* Top() const { return elems_[0]; } + + void Pop() { Remove(Top()); } + + int Size() const { return elems_.size(); } + + bool IsEmpty() const { return elems_.size() == 0; } + + void Clear() { elems_.resize(0); } + + void CheckValid() const { + for (int i = 0; i < Size(); ++i) { + int left_child = 1 + 2 * i; + if (left_child < Size()) { + CHECK_GE(elems_[i], elems_[left_child]); + } + int right_child = left_child + 1; + if (right_child < Size()) { + CHECK_GE(elems_[i], elems_[right_child]); + } + } + } + private: + void AdjustUpwards(int i) { + T* const t = elems_[i]; + while (i > 0) { + const int parent = (i - 1) / 2; + if (!(*elems_[parent] < *t)) { + break; + } + elems_[i] = elems_[parent]; + elems_[i]->SetHeapIndex(i); + i = parent; + } + elems_[i] = t; + t->SetHeapIndex(i); + } + + void AdjustDownwards(int i) { + T* const t = elems_[i]; + while (true) { + const int left_child = 1 + 2 * i; + if (left_child >= elems_.size()) { + break; + } + const int right_child = left_child + 1; + const int next_i = (right_child < elems_.size() && + *elems_[left_child] < *elems_[right_child]) ? + right_child : + left_child; + if (!(*t < *elems_[next_i])) { + break; + } + elems_[i] = elems_[next_i]; + elems_[i]->SetHeapIndex(i); + i = next_i; + } + elems_[i] = t; + t->SetHeapIndex(i); + } + + vector elems_; + DISALLOW_COPY_AND_ASSIGN(AdjustablePriorityQueue); +}; +} // namespace operations_research + +#endif // BASE_ADJUSTABLE_PRIORITY_QUEUE_H diff --git a/graph/bellman_ford.cc b/graph/bellman_ford.cc new file mode 100644 index 0000000000..b9c492b893 --- /dev/null +++ b/graph/bellman_ford.cc @@ -0,0 +1,123 @@ +// Copyright 2010 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "base/callback.h" +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/scoped_ptr.h" + +#include "graph/graph.h" + +DECLARE_int32(shortestpaths_disconnected_distance); + +namespace operations_research { +class BellmanFord { + public: + static const int64 kInfinity = kint64max / 2; + + BellmanFord(int node_count, + int start_node, + ResultCallback2* const graph) + : node_count_(node_count), + start_node_(start_node), + graph_(graph), + distance_(new int64[node_count_]), + predecessor_(new int[node_count_]) { + graph->CheckIsRepeatable(); + } + bool ShortestPath(int end_node, vector* nodes); + private: + void Initialize(); + void Update(); + bool Check() const; + void FindPath(int dest, vector* nodes) const; + + const int node_count_; + const int start_node_; + scoped_ptr > graph_; + scoped_array distance_; + scoped_array predecessor_; +}; + +void BellmanFord::Initialize() { + for (int i = 0; i < node_count_; i++) { + distance_[i] = kint64max / 2; + predecessor_[i] = -1; + } + distance_[start_node_] = 0; +} + +void BellmanFord::Update() { + for (int i = 0; i < node_count_ - 1; i++) { + for (int u = 0; u < node_count_; u++) { + for (int v = 0; v < node_count_; v++) { + const int64 graph_u_v = graph_->Run(u, v); + if (graph_u_v != FLAGS_shortestpaths_disconnected_distance) { + const int64 other_distance = distance_[u] + graph_u_v; + if (distance_[v] > other_distance) { + distance_[v] = other_distance; + predecessor_[v] = u; + } + } + } + } + } +} + +bool BellmanFord::Check() const { + for (int u = 0; u < node_count_; u++) { + for (int v = 0; v < node_count_; v++) { + const int graph_u_v = graph_->Run(u, v); + if (graph_u_v != FLAGS_shortestpaths_disconnected_distance) { + if (distance_[v] > distance_[u] + graph_u_v) { + return false; + } + } + } + } + return true; +} + +void BellmanFord::FindPath(int dest, vector* nodes) const { + int j = dest; + nodes->push_back(j); + while (predecessor_[j] != -1) { + nodes->push_back(predecessor_[j]); + j = predecessor_[j]; + } +} + +bool BellmanFord::ShortestPath(int end_node, vector* nodes) { + Initialize(); + Update(); + if (distance_[end_node] == kInfinity) { + return false; + } + if (!Check()) { + return false; + } + FindPath(end_node, nodes); + return true; +} + +bool BellmanFordShortestPath(int node_count, + int start_node, + int end_node, + ResultCallback2* const graph, + vector* nodes) { + BellmanFord bf(node_count, start_node, graph); + return bf.ShortestPath(end_node, nodes); +} +} // namespace operations_research diff --git a/graph/dijkstra.cc b/graph/dijkstra.cc new file mode 100644 index 0000000000..ae11117a8c --- /dev/null +++ b/graph/dijkstra.cc @@ -0,0 +1,166 @@ +// Copyright 2010 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "base/callback.h" +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/scoped_ptr.h" + +#include "graph/shortestpaths.h" +#include "base/adjustable_priority_queue.h" + + +DECLARE_int32(shortestpaths_disconnected_distance); + +namespace operations_research { +namespace { + +// Priority queue element +class Element { + public: + Element() : heap_index_(-1), distance_(0), node_(-1) {} + bool operator <(const Element& other) const { + return distance_ > other.distance_; + } + void SetHeapIndex(int h) { heap_index_ = h; } + int GetHeapIndex() const { return heap_index_; } + void set_distance(int64 distance) { distance_ = distance; } + int64 distance() const { return distance_; } + void set_node(int node) { node_ = node; } + int node() const { return node_; } + private: + int heap_index_; + int64 distance_; + int node_; +}; +} // namespace + +class DijkstraSP { + public: + static const int64 kInfinity = kint64max / 2; + + DijkstraSP(int node_count, + int start_node, + ResultCallback2* const graph) + : node_count_(node_count), + start_node_(start_node), + graph_(graph), + predecessor_(new int[node_count]), + elements_(node_count) { + graph->CheckIsRepeatable(); + } + bool ShortestPath(int end_node, vector* nodes); + private: + void Initialize(); + int SelectClosestNode(int64* distance); + void Update(int label); + void FindPath(int dest, vector* nodes); + + const int node_count_; + const int start_node_; + scoped_ptr > graph_; + scoped_array predecessor_; + AdjustablePriorityQueue frontier_; + vector elements_; + hash_set not_visited_; + hash_set added_to_the_frontier_; +}; + +void DijkstraSP::Initialize() { + for (int i = 0; i < node_count_; i++) { + elements_[i].set_node(i); + if (i == start_node_) { + predecessor_[i] = -1; + elements_[i].set_distance(0); + frontier_.Add(&elements_[i]); + } else { + elements_[i].set_distance(kInfinity); + predecessor_[i] = start_node_; + not_visited_.insert(i); + } + } +} + +int DijkstraSP::SelectClosestNode(int64* distance) { + const int node = frontier_.Top()->node(); + *distance = frontier_.Top()->distance(); + frontier_.Pop(); + not_visited_.erase(node); + added_to_the_frontier_.erase(node); + return node; +} + +void DijkstraSP::Update(int node) { + for (hash_set::const_iterator it = not_visited_.begin(); + it != not_visited_.end(); + ++it) { + const int other_node = *it; + const int64 graph_node_i = graph_->Run(node, other_node); + if (graph_node_i != FLAGS_shortestpaths_disconnected_distance) { + if (added_to_the_frontier_.find(other_node) == + added_to_the_frontier_.end()) { + frontier_.Add(&elements_[other_node]); + added_to_the_frontier_.insert(other_node); + } + const int64 other_distance = elements_[node].distance() + graph_node_i; + if (elements_[other_node].distance() > other_distance) { + elements_[other_node].set_distance(other_distance); + frontier_.NoteChangedPriority(&elements_[other_node]); + predecessor_[other_node] = node; + } + } + } +} + +void DijkstraSP::FindPath(int dest, vector* nodes) { + int j = dest; + nodes->push_back(j); + while (predecessor_[j] != -1) { + nodes->push_back(predecessor_[j]); + j = predecessor_[j]; + } +} + +bool DijkstraSP::ShortestPath(int end_node, vector* nodes) { + Initialize(); + bool found = false; + while (!frontier_.IsEmpty()) { + int64 distance; + int node = SelectClosestNode(&distance); + if (distance == kInfinity) { + found = false; + break; + } else if (node == end_node) { + found = true; + break; + } + Update(node); + } + if (found) { + FindPath(end_node, nodes); + } + return found; +} + +bool DijkstraShortestPath(int node_count, + int start_node, + int end_node, + ResultCallback2* const graph, + vector* nodes) { + DijkstraSP bf(node_count, start_node, graph); + return bf.ShortestPath(end_node, nodes); +} +} // namespace operations_research diff --git a/graph/shortestpaths.h b/graph/shortestpaths.h new file mode 100644 index 0000000000..9db1eaf91e --- /dev/null +++ b/graph/shortestpaths.h @@ -0,0 +1,60 @@ +// Copyright 2010 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains various shortestpaths utilities. + +#ifndef GRAPH_SHORTESTPATHS_H_ +#define GRAPH_SHORTESTPATHS_H_ + +#include +#include + +#include "base/callback-types.h" +#include "base/integral_types.h" +#include "base/macros.h" +#include "base/scoped_ptr.h" + + +namespace operations_research { + + +// Dijsktra Shortest path with callback based description of the graph. +// The callback returns the distance between two nodes, a distance of +// 'shortestpaths_disconnected_distance' (flag) indicates no arcs between these +// two nodes. Ownership of the callback is taken by the function that will +// delete it in the end. +// This function returns true if 'start_node' and 'end_node' are connected, +// false otherwise. +bool DijkstraShortestPath(int node_count, + int start_node, + int end_node, + ResultCallback2* const graph, + vector* nodes); + +// Bellman-Ford Shortest path with callback-based description of the graph. +// The callback returns the distance between two nodes, a distance of +// 'shortestpaths_disconnected_distance' (flag) indicates no arcs between +// these two nodes. +// Ownership of the callback is taken by the function that will delete it +// in the end. +// This function returns true if 'start_node' and 'end_node' are connected, +// false otherwise. If true, it will fill the 'nodes' vector with the +// sequence of nodes on the shortest path between 'start_node' and 'end_node'. +bool BellmanFordShortestPath(int node_count, + int start_node, + int end_node, + ResultCallback2* const graph, + vector* nodes); +} // namespace operations_research + +#endif // GRAPH_SHORTESTPATHS_H_