Update Dijkstra's algorithm to take any weights that satisfy the new Weight trait
This commit is contained in:
+46
-15
@@ -32,19 +32,46 @@ use priority_queue::PriorityQueue;
|
|||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
use crate::maps::ElementMap;
|
use crate::maps::ElementMap;
|
||||||
use crate::traits::{GraphTopology, Incidence, IncidenceCursor};
|
use crate::traits::{GraphTopology, Incidence, IncidenceCursor};
|
||||||
|
|
||||||
|
/// Trait that edge weights for [`dijkstra`] and its variants need to satisfy.
|
||||||
|
///
|
||||||
|
/// [`Default`] is used as the starting distance for `source`, so it must behave as the additive
|
||||||
|
/// identity (typically zero) for accumulation via [`Add`] to give correct results.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use grapherity::prelude::*;
|
||||||
|
/// # use grapherity::algorithms::dijkstra;
|
||||||
|
/// # use grapherity::models::Graph;
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// let mut graph = Graph::new();
|
||||||
|
/// let source = graph.add_vertex();
|
||||||
|
/// let target = graph.add_vertex();
|
||||||
|
/// let e = graph.add_edge(source, target);
|
||||||
|
/// let mut travel_time = graph.edge_map(Duration::ZERO);
|
||||||
|
/// travel_time[e] = Duration::from_secs(5);
|
||||||
|
///
|
||||||
|
/// let result = dijkstra(&graph, source, |e| travel_time[e]);
|
||||||
|
/// assert_eq!(result.distances[target], Some(Duration::from_secs(5)));
|
||||||
|
/// ```
|
||||||
|
pub trait Weight: Copy + Ord + Add<Output = Self> + Default {}
|
||||||
|
|
||||||
|
impl<T: Copy + Ord + Add<Output = T> + Default> Weight for T {}
|
||||||
|
|
||||||
/// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
|
/// Return data type for [`dijkstra`] and [`dijkstra_unweighted`].
|
||||||
pub struct DijkstraResult<V: Copy> {
|
pub struct DijkstraResult<V: Copy, W: Weight> {
|
||||||
/// Vertex map of minimum distances from a given `source` vertex.
|
/// Vertex map of minimum distances from a given `source` vertex.
|
||||||
pub distances: ElementMap<V, Option<u32>>,
|
pub distances: ElementMap<V, Option<W>>,
|
||||||
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
/// Vertex map of predecessors on some shortest path from a given `source` vertex.
|
||||||
pub predecessors: ElementMap<V, Option<V>>,
|
pub predecessors: ElementMap<V, Option<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Generalize the return type of the weight function.
|
|
||||||
// TODO: A Fibonacci heap would lower complexity to O(|E| + |V| log |V|) by making decrease-key O(1) amortized instead of O(log |V|). No standard Rust implementation exists; high constant factors may negate the asymptotic gain in practice.
|
// TODO: A Fibonacci heap would lower complexity to O(|E| + |V| log |V|) by making decrease-key O(1) amortized instead of O(log |V|). No standard Rust implementation exists; high constant factors may negate the asymptotic gain in practice.
|
||||||
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
|
/// [Dijkstra's algorithm] with custom edge weights, returns minimum distances and predecessors.
|
||||||
///
|
///
|
||||||
@@ -78,11 +105,15 @@ pub struct DijkstraResult<V: Copy> {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
||||||
pub fn dijkstra<G, W>(graph: &G, source: G::Vertex, weights: W) -> DijkstraResult<G::Vertex>
|
pub fn dijkstra<G, W>(
|
||||||
|
graph: &G,
|
||||||
|
source: G::Vertex,
|
||||||
|
weights: impl Fn(G::Edge) -> W,
|
||||||
|
) -> DijkstraResult<G::Vertex, W>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
G::Vertex: Hash,
|
G::Vertex: Hash,
|
||||||
W: Fn(G::Edge) -> u32,
|
W: Weight,
|
||||||
{
|
{
|
||||||
let mut predecessors = graph.vertex_map(None);
|
let mut predecessors = graph.vertex_map(None);
|
||||||
let distances = dijkstra_impl(graph, source, weights, |adjacent, predecessor| {
|
let distances = dijkstra_impl(graph, source, weights, |adjacent, predecessor| {
|
||||||
@@ -127,12 +158,12 @@ where
|
|||||||
pub fn dijkstra_distances<G, W>(
|
pub fn dijkstra_distances<G, W>(
|
||||||
graph: &G,
|
graph: &G,
|
||||||
source: G::Vertex,
|
source: G::Vertex,
|
||||||
weights: W,
|
weights: impl Fn(G::Edge) -> W,
|
||||||
) -> ElementMap<G::Vertex, Option<u32>>
|
) -> ElementMap<G::Vertex, Option<W>>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
G::Vertex: Hash,
|
G::Vertex: Hash,
|
||||||
W: Fn(G::Edge) -> u32,
|
W: Weight,
|
||||||
{
|
{
|
||||||
dijkstra_impl(graph, source, weights, |_, _| {})
|
dijkstra_impl(graph, source, weights, |_, _| {})
|
||||||
}
|
}
|
||||||
@@ -168,7 +199,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
/// [Dijkstra's algorithm]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
|
||||||
pub fn dijkstra_unweighted<G>(graph: &G, source: G::Vertex) -> DijkstraResult<G::Vertex>
|
pub fn dijkstra_unweighted<G>(graph: &G, source: G::Vertex) -> DijkstraResult<G::Vertex, u32>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
G::Vertex: Hash,
|
G::Vertex: Hash,
|
||||||
@@ -220,24 +251,24 @@ where
|
|||||||
fn dijkstra_impl<G, W, F>(
|
fn dijkstra_impl<G, W, F>(
|
||||||
graph: &G,
|
graph: &G,
|
||||||
source: G::Vertex,
|
source: G::Vertex,
|
||||||
weights: W,
|
weights: impl Fn(G::Edge) -> W,
|
||||||
mut on_relax: F,
|
mut on_relax: F,
|
||||||
) -> ElementMap<G::Vertex, Option<u32>>
|
) -> ElementMap<G::Vertex, Option<W>>
|
||||||
where
|
where
|
||||||
G: GraphTopology,
|
G: GraphTopology,
|
||||||
G::Vertex: Hash,
|
G::Vertex: Hash,
|
||||||
W: Fn(G::Edge) -> u32,
|
W: Weight,
|
||||||
F: FnMut(G::Vertex, G::Vertex),
|
F: FnMut(G::Vertex, G::Vertex),
|
||||||
{
|
{
|
||||||
let mut distances = graph.vertex_map(None);
|
let mut distances = graph.vertex_map(None);
|
||||||
let mut heap = PriorityQueue::new();
|
let mut heap = PriorityQueue::new();
|
||||||
|
|
||||||
distances[source] = Some(0);
|
distances[source] = Some(W::default());
|
||||||
heap.push(source, Reverse(0u32));
|
heap.push(source, Reverse(W::default()));
|
||||||
|
|
||||||
while let Some((v, Reverse(v_distance))) = heap.pop() {
|
while let Some((v, Reverse(v_distance))) = heap.pop() {
|
||||||
for Incidence { vertex: u, edge: e } in graph.incidences(v) {
|
for Incidence { vertex: u, edge: e } in graph.incidences(v) {
|
||||||
let new_distance = v_distance + weights(e);
|
let new_distance = weights(e) + v_distance;
|
||||||
if match distances[u] {
|
if match distances[u] {
|
||||||
None => true,
|
None => true,
|
||||||
Some(old_distance) if old_distance > new_distance => true,
|
Some(old_distance) if old_distance > new_distance => true,
|
||||||
|
|||||||
+5
-3
@@ -173,7 +173,7 @@ where
|
|||||||
fixtures::assert_standard_unweighted_distances_v0(|v| distances[v], &vertices);
|
fixtures::assert_standard_unweighted_distances_v0(|v| distances[v], &vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_single_vertex<G: GraphTopology>(result: &DijkstraResult<G::Vertex>, v: G::Vertex)
|
fn assert_single_vertex<G: GraphTopology>(result: &DijkstraResult<G::Vertex, u32>, v: G::Vertex)
|
||||||
where
|
where
|
||||||
G::Vertex: Debug,
|
G::Vertex: Debug,
|
||||||
{
|
{
|
||||||
@@ -195,8 +195,10 @@ fn assert_distances_single_vertex<G: GraphTopology>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_disconnected<G: GraphTopology>(result: &DijkstraResult<G::Vertex>, vertices: &[G::Vertex])
|
fn assert_disconnected<G: GraphTopology>(
|
||||||
where
|
result: &DijkstraResult<G::Vertex, u32>,
|
||||||
|
vertices: &[G::Vertex],
|
||||||
|
) where
|
||||||
G::Vertex: Debug,
|
G::Vertex: Debug,
|
||||||
{
|
{
|
||||||
assert_distances_disconnected::<G>(&result.distances, &vertices);
|
assert_distances_disconnected::<G>(&result.distances, &vertices);
|
||||||
|
|||||||
Reference in New Issue
Block a user