1
0
Vsevolod Levitan 7 мес. назад
Сommit
1fe0c55e67
7 измененных файлов с 170 добавлено и 0 удалено
  1. 2 0
      .gitignore
  2. 7 0
      dijkstra/Cargo.lock
  3. 13 0
      dijkstra/Cargo.toml
  4. 35 0
      dijkstra/README.md
  5. 1 0
      dijkstra/example.cmd
  6. 2 0
      dijkstra/example.sh
  7. 110 0
      dijkstra/src/main.rs

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*/target
+*/target/*

+ 7 - 0
dijkstra/Cargo.lock

@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "dijkstra"
+version = "0.1.0"

+ 13 - 0
dijkstra/Cargo.toml

@@ -0,0 +1,13 @@
+[package]
+name = "dijkstra"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+
+[profile.release]
+strip = true
+opt-level = "z"
+lto = true
+codegen-units = 1
+panic = "abort"

+ 35 - 0
dijkstra/README.md

@@ -0,0 +1,35 @@
+# Алгоритм Дейкстры.
+## Пример
+![Пример графа](https://media.geeksforgeeks.org/wp-content/uploads/20240111182238/Working-of-Dijkstras-Algorithm-768.jpg)
+
+Команда запуска с cargo: `cargo run -- 0 0-1=4 0-7=8 1-7=11 1-2=8 7-6=1 7-8=7 2-8=2 6-8=6 6-5=2 2-5=4 2-3=7 5-3=14 5-4=10 3-4=9`
+
+Команда запуска исходника: `dijkstra.exe 0 0-1=4 0-7=8 1-7=11 1-2=8 7-6=1 7-8=7 2-8=2 6-8=6 6-5=2 2-5=4 2-3=7 5-3=14 5-4=10 3-4=9`
+
+Вывод:
+```
+Total edges: 14
+Starting vertex: 0
+Finding paths...
+Length of path from 0 to 0 is 0
+Length of path from 0 to 1 is 4
+Length of path from 0 to 2 is 12
+Length of path from 0 to 3 is 19
+Length of path from 0 to 4 is 21
+Length of path from 0 to 5 is 11
+Length of path from 0 to 6 is 9
+Length of path from 0 to 7 is 8
+Length of path from 0 to 8 is 14
+```
+
+## Аргументы запуска
+1. Стартовая вершина
+2. Список рёбер через пробел в формате "A-B=C" где A, B - символьные названия вершин, C - длина ребра
+Пример: `dijkstra.exe 0 0-1=4 0-7=8 1-7=11 1-2=8 7-6=1 7-8=7 2-8=2 6-8=6 6-5=2 2-5=4 2-3=7 5-3=14 5-4=10 3-4=9`
+
+## Запуск
+### При наличии cargo (комплекта сборки для Rust):
+`cargo run -- <старт> <рёбра>`
+
+### При отсутствии cargo:
+Онлайн-компилятор: [OnlineGDB](https://onlinegdb.com/XJxGihSwo)

+ 1 - 0
dijkstra/example.cmd

@@ -0,0 +1 @@
+cargo run -q -- 0 0-1=4 0-7=8 1-7=11 1-2=8 7-6=1 7-8=7 2-8=2 6-8=6 6-5=2 2-5=4 2-3=7 5-3=14 5-4=10 3-4=9

+ 2 - 0
dijkstra/example.sh

@@ -0,0 +1,2 @@
+#! /bin/bash
+cargo run -q -- 0 0-1=4 0-7=8 1-7=11 1-2=8 7-6=1 7-8=7 2-8=2 6-8=6 6-5=2 2-5=4 2-3=7 5-3=14 5-4=10 3-4=9

+ 110 - 0
dijkstra/src/main.rs

@@ -0,0 +1,110 @@
+use std::collections::BTreeMap;
+use std::ops::Add;
+
+// Граф - дерево деревьев
+type Graph<V, E> = BTreeMap<V, BTreeMap<V, E>>;
+
+pub fn dijkstra<V: Ord + Copy, E: Ord + Copy + Add<Output = E>>(
+    graph: &Graph<V, E>,
+    start: V,
+) -> BTreeMap<V, Option<(V, E)>> {
+    let mut res = BTreeMap::new();
+    let mut queue = BTreeMap::new();
+
+    // Путь от начала до начала равен 0
+    res.insert(start, None);
+
+    for (new, weight) in &graph[&start] {
+        // Сохраняем путь
+        res.insert(*new, Some((start, *weight)));
+        // Сохраняем в очередь
+        queue.insert(*new, *weight);
+    }
+
+    // Проходим по всем элементам очереди
+    while let Some((vertex, path_weight)) = queue.pop_first() {
+        // Проходим по всем следующим связанным элементам
+        for (next, weight) in &graph[&vertex] {
+            // Считаем итоговую сумму на данный момент
+            let new_weight = path_weight + *weight;
+            // Ищем следующий элемент в матрице
+            match res.get(next) {
+                // Если следующий элемент есть в матрице и имеет расстояние, меньшее найденного на этой итерации - не делаем ничего
+                Some(Some((_, weight_next))) if new_weight >= *weight_next => {}
+                // Если следующий элемент начальный - не делаем ничего
+                Some(None) => {}
+                // Если следующего элемента нет - сохраняем в матрицу и очередь с новым весом
+                _ => {
+                    res.insert(*next, Some((vertex, new_weight)));
+                    queue.insert(*next, new_weight);
+                }
+            }
+        }
+    }
+
+    res
+}
+
+fn insert_edge<V: Ord + Copy, E: Ord + Copy>(graph: &mut Graph<V, E>, v1: V, v2: V, c: E) {
+    // Добавляем А->Б
+    graph.entry(v1).or_default().insert(v2, c);
+    // Добавляем Б-А
+    graph.entry(v2).or_default().insert(v1, c);
+}
+
+fn main() {
+    let args: Vec<String> = std::env::args().skip(1).collect();
+
+    // Если аргументы пусты или -h/--help
+    if args.is_empty() || args[0] == "--help" || args[0] == "-h" {
+        println!("Usage: {} <start> <edges>.", std::env::args().nth(0).unwrap_or("dijkstra".to_string()));
+        println!("Each vertex is specified as a single latin character");
+        println!("Edges are separated by a whitespace and formatted as A-B=C where A, B are vertex names and C is the length of the edge");
+        return;
+    }
+
+    // Стартовая вершина - первый аргумент
+    let start = args[0].chars().nth(0).expect("No start specified");
+
+    // Парсим аргументы в список рёбер
+    let edges: Vec<(char, char, u128)> = args
+        .iter()
+        .skip(1) // Пропускаем первый аргумент - стартовую вершину
+        .map(|s| {
+            // Разбиваем аргумент по символам - и =
+            let parts: Vec<&str> = s.split(&['-', '='][..]).collect();
+            (
+                parts[0].chars().next().unwrap(), // Первая вершина
+                parts[1].chars().next().unwrap(), // Вторая вершина
+                parts[2].parse().unwrap(),        // Длина
+            )
+        })
+        .collect();
+
+    // Создаем граф
+    let mut graph = Graph::new();
+
+    // Для каждого найденного ребра из вершины добавляем в граф
+    for edge in &edges {
+        insert_edge(&mut graph, edge.0.clone(), edge.1.clone(), edge.2);
+    }
+
+    println!(
+        "Total edges: {}\nStarting vertex: {}\nFinding paths...",
+        edges.len(),
+        start
+    );
+
+    // Считаем длины путей
+    let paths = dijkstra(&graph, start);
+
+    // Выводим длину для каждого пути
+    for path in &paths {
+        let to = path.0;
+        if let Some(second) = path.1 {
+            println!("Length of path from {} to {} is {}", start, to, second.1);
+        } else {
+            println!("Length of path from {} to {} is 0", start, to);
+        }
+    }
+}