add dijkstra module

This commit is contained in:
Dr. Matthias Ratajczak
2022-09-26 17:35:45 +02:00
parent 2e9da0d7aa
commit 6e30da5371
2 changed files with 152 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
use std::fmt::Debug;
use std::fmt::Display;
use std::{collections::HashSet, ops::Add};
type Coordinate = (usize, usize);
const START: Coordinate = (0, 0);
pub trait MatrixInner<T>
where
T: Add + PartialOrd + Debug + Copy,
{
fn height(&self) -> usize;
fn width(&self) -> usize;
fn get(&self, coordinate: &Coordinate) -> &Option<Node<T>>;
fn get_mut(&mut self, coordinate: &Coordinate) -> &mut Option<Node<T>>;
}
pub trait Matrix<T>: MatrixInner<T> + Debug
where
T: Add + PartialOrd + Debug + Copy,
{
}
#[derive(Debug)]
pub struct Dijkstra<T> {
matrix: Box<dyn Matrix<T>>,
unvisited: HashSet<Coordinate>,
}
impl<T> Dijkstra<T>
where
T: PartialOrd + Add<Output = T> + Display + Copy + Debug,
{
pub fn new(matrix: Box<dyn Matrix<T>>) -> Self {
let unvisited = Self::initialize_unvisited_set(&*matrix, START);
Self { matrix, unvisited }
}
pub fn solve(&mut self) {
while !self.unvisited.is_empty() {
let coord = self.get_unvisited_coord_with_smallest_distance_from_start();
let neighbours = self.get_neighbours(&coord);
let previous_distance = *self
.matrix
.get(&coord)
.as_ref()
.unwrap()
.distance
.as_ref()
.unwrap();
for neighbour in neighbours {
self.update_neighbour(neighbour, coord, previous_distance);
}
}
}
fn get_unvisited_coord_with_smallest_distance_from_start(&mut self) -> Coordinate {
let coord = self
.unvisited
.iter()
.filter_map(|c| {
let node = self.matrix.get(c).as_ref().unwrap();
node.distance.map(|value| (c, value))
})
.reduce(|(c0, v0), (c1, v1)| if v0 < v1 { (c0, v0) } else { (c1, v1) })
.map(|(c, _)| *c)
.unwrap_or((0, 0));
self.unvisited.remove(&coord);
coord
}
fn get_neighbours(&self, coordinate: &Coordinate) -> Vec<Coordinate> {
let (x0, y0) = coordinate;
let mut result = vec![];
if *x0 < self.matrix.width() - 1 {
result.push((*x0 + 1, *y0));
}
if *y0 < self.matrix.height() - 1 {
result.push((*x0, *y0 + 1));
}
result
}
fn update_neighbour(&mut self, next: Coordinate, previous: Coordinate, previous_distance: T) {
let mut next_node = self.matrix.get_mut(&next).as_mut().unwrap();
let new_distance = previous_distance + next_node.value;
match next_node.distance {
None => {
next_node.distance = Some(new_distance);
next_node.previous = Some(previous);
}
Some(old_distance) => {
if new_distance < old_distance {
next_node.distance = Some(new_distance);
next_node.previous = Some(previous);
}
}
}
}
pub fn show(&self) {
let result = self
.matrix
.get(&(self.matrix.height() - 1, self.matrix.width() - 1))
.as_ref()
.unwrap()
.distance
.as_ref()
.unwrap();
println!("{}", result);
}
fn initialize_unvisited_set(matrix: &dyn Matrix<T>, start: Coordinate) -> HashSet<Coordinate> {
let unvisited: HashSet<_> = (0..matrix.height())
.flat_map(|x| (0..matrix.width()).zip([x].into_iter().cycle()))
.filter(|value| *value != start)
.collect();
unvisited
}
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct Node<T>
where
T: Add + PartialOrd,
{
value: T,
distance: Option<T>,
previous: Option<Coordinate>,
}
impl<T> From<T> for Node<T>
where
T: Add + PartialOrd,
{
fn from(value: T) -> Self {
Node {
value,
distance: None,
previous: None,
}
}
}