Add shortest paths algorithms (Bellman-Ford and Dijkstra)

This commit is contained in:
lperron@google.com
2010-09-21 14:57:35 +00:00
parent a216159f76
commit d6a4fedb4f
6 changed files with 528 additions and 2 deletions

View File

@@ -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=\

View File

@@ -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=\

View File

@@ -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 <functional>
#include <list>
#include <vector>
#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<typename T> 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<T*> elems_;
DISALLOW_COPY_AND_ASSIGN(AdjustablePriorityQueue);
};
} // namespace operations_research
#endif // BASE_ADJUSTABLE_PRIORITY_QUEUE_H

123
graph/bellman_ford.cc Normal file
View File

@@ -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 <vector>
#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<int64, int, int>* 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<int>* nodes);
private:
void Initialize();
void Update();
bool Check() const;
void FindPath(int dest, vector<int>* nodes) const;
const int node_count_;
const int start_node_;
scoped_ptr<ResultCallback2<int64, int, int> > graph_;
scoped_array<int64> distance_;
scoped_array<int> 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<int>* 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<int>* 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<int64, int, int>* const graph,
vector<int>* nodes) {
BellmanFord bf(node_count, start_node, graph);
return bf.ShortestPath(end_node, nodes);
}
} // namespace operations_research

166
graph/dijkstra.cc Normal file
View File

@@ -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 <hash_set>
#include <vector>
#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<int64, int, int>* 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<int>* nodes);
private:
void Initialize();
int SelectClosestNode(int64* distance);
void Update(int label);
void FindPath(int dest, vector<int>* nodes);
const int node_count_;
const int start_node_;
scoped_ptr<ResultCallback2<int64, int, int> > graph_;
scoped_array<int> predecessor_;
AdjustablePriorityQueue<Element> frontier_;
vector<Element> elements_;
hash_set<int> not_visited_;
hash_set<int> 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<int>::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<int>* 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<int>* 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<int64, int, int>* const graph,
vector<int>* nodes) {
DijkstraSP bf(node_count, start_node, graph);
return bf.ShortestPath(end_node, nodes);
}
} // namespace operations_research

60
graph/shortestpaths.h Normal file
View File

@@ -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 <string>
#include <vector>
#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<int64, int, int>* const graph,
vector<int>* 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<int64, int, int>* const graph,
vector<int>* nodes);
} // namespace operations_research
#endif // GRAPH_SHORTESTPATHS_H_