Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
dijkstra_example.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
111#include <iostream>
112#include <iomanip>
113#include <string>
114#include <vector>
115#include <chrono>
116#include <random>
117#include <limits>
118
119#include <tpl_graph.H>
120#include <Dijkstra.H>
121
122using namespace std;
123using namespace Aleph;
124
125// =============================================================================
126// Type Definitions
127// =============================================================================
128
131
134
137
138// =============================================================================
139// Distance Accessor
140// =============================================================================
141
151{
153
154 double operator()(CityGraph::Arc* arc) const
155 {
156 return arc->get_info();
157 }
158};
159
160// =============================================================================
161// Graph Building Utilities
162// =============================================================================
163
185{
186 CityGraph g;
187
188 // Insert cities (nodes)
189 nodes.push_back(g.insert_node("Alpha")); // 0
190 nodes.push_back(g.insert_node("Beta")); // 1
191 nodes.push_back(g.insert_node("Gamma")); // 2
192 nodes.push_back(g.insert_node("Delta")); // 3
193 nodes.push_back(g.insert_node("Epsilon")); // 4
194 nodes.push_back(g.insert_node("Zeta")); // 5
195 nodes.push_back(g.insert_node("Eta")); // 6
196 nodes.push_back(g.insert_node("Theta")); // 7
197
198 // Lambda to add bidirectional roads
199 auto add_road = [&](int from, int to, double dist) {
200 g.insert_arc(nodes[from], nodes[to], dist);
201 g.insert_arc(nodes[to], nodes[from], dist);
202 };
203
204 // Add roads
205 add_road(0, 1, 100); // Alpha - Beta
206 add_road(1, 2, 150); // Beta - Gamma
207 add_road(0, 3, 200); // Alpha - Delta
208 add_road(1, 4, 50); // Beta - Epsilon
209 add_road(2, 5, 100); // Gamma - Zeta
210 add_road(3, 4, 80); // Delta - Epsilon
211 add_road(4, 5, 120); // Epsilon - Zeta
212 add_road(3, 6, 300); // Delta - Eta
213 add_road(5, 7, 90); // Zeta - Theta
214 add_road(6, 7, 250); // Eta - Theta
215
216 return g;
217}
218
229 double max_weight, unsigned seed = 42)
230{
231 CityGraph g;
233
234 mt19937 rng(seed);
235 uniform_real_distribution<double> prob_dist(0.0, 1.0);
237
238 // Create nodes
239 for (int i = 0; i < num_nodes; ++i)
240 nodes.push_back(g.insert_node("N" + to_string(i)));
241
242 // Create edges with given probability
243 for (int i = 0; i < num_nodes; ++i)
244 for (int j = i + 1; j < num_nodes; ++j)
245 if (prob_dist(rng) < edge_probability)
246 {
247 double w = weight_dist(rng);
248 g.insert_arc(nodes[i], nodes[j], w);
249 g.insert_arc(nodes[j], nodes[i], w);
250 }
251
252 return g;
253}
254
255// =============================================================================
256// Visualization Utilities
257// =============================================================================
258
263{
264 cout << "\n┌─────────────────────────────────────────┐\n";
265 cout << "│ City Road Network │\n";
266 cout << "├─────────────────────────────────────────┤\n";
267 cout << "│ Cities: " << setw(3) << g.get_num_nodes()
268 << " │\n";
269 cout << "│ Roads: " << setw(3) << g.get_num_arcs() / 2
270 << " (bidirectional) │\n";
271 cout << "└─────────────────────────────────────────┘\n\n";
272
273 cout << "Connections:\n";
274 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
275 {
276 auto* node = nit.get_curr();
277 cout << " " << setw(8) << left << node->get_info() << right << " → ";
278 bool first = true;
279 for (auto ait = g.get_out_it(node); ait.has_curr(); ait.next())
280 {
281 auto* arc = ait.get_curr();
282 auto* tgt = g.get_tgt_node(arc);
283 if (!first) cout << ", ";
284 cout << tgt->get_info() << "(" << arc->get_info() << ")";
285 first = false;
286 }
287 cout << "\n";
288 }
289}
290
295{
296 if (path.size() == 0)
297 {
298 cout << " (empty path)\n";
299 return;
300 }
301
302 // Print route using for_each_node
303 cout << "\n Route: ";
304 bool first = true;
305 path.for_each_node([&first](CityGraph::Node* node) {
306 if (!first) cout << " → ";
307 cout << node->get_info();
308 first = false;
309 });
310 cout << "\n\n";
311
312 // Step by step using for_each_arc (safe iteration)
313 if (path.size() <= 1)
314 return;
315
316 cout << " Step-by-step:\n";
317 cout << " ┌──────────────────────────────────────────────────┐\n";
318 cout << " │ From Distance To Cumulative│\n";
319 cout << " ├──────────────────────────────────────────────────┤\n";
320
321 double cumulative = 0;
322 path.for_each_arc([&](CityGraph::Arc* arc) {
323 cumulative += arc->get_info();
324 cout << " │ " << setw(8) << left << g.get_src_node(arc)->get_info()
325 << " ──" << setw(5) << right << arc->get_info() << " km──▶ "
326 << setw(8) << left << g.get_tgt_node(arc)->get_info()
327 << right << setw(7) << cumulative << " km │\n";
328 });
329 cout << " └──────────────────────────────────────────────────┘\n";
330}
331
332// =============================================================================
333// Timing Utility
334// =============================================================================
335
336template <typename Func>
338{
339 auto start = chrono::high_resolution_clock::now();
340 f();
341 auto end = chrono::high_resolution_clock::now();
342 return chrono::duration<double, milli>(end - start).count();
343}
344
345// =============================================================================
346// Main Demonstration
347// =============================================================================
348
349int main()
350{
351 cout << "╔══════════════════════════════════════════════════════════════════╗\n";
352 cout << "║ Dijkstra's Shortest Path Algorithm - Example ║\n";
353 cout << "╚══════════════════════════════════════════════════════════════════╝\n";
354
355 // =========================================================================
356 // Part 1: Basic Usage
357 // =========================================================================
358
359 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
360 cout << "Part 1: Basic Usage - Finding Shortest Path\n";
361 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
362
365
366 print_graph(g);
367
368 // Define source and destination
369 auto* source = nodes[0]; // Alpha
370 auto* dest = nodes[7]; // Theta
371
372 cout << "\n▶ Finding shortest path from " << source->get_info()
373 << " to " << dest->get_info() << ":\n";
374
375 // Create Dijkstra solver with default Binary Heap
377
378 // Find shortest path
379 Path<CityGraph> path(g);
380 double distance = dijkstra.find_min_path(g, source, dest, path);
381
382 cout << "\n Total distance: " << distance << " km\n";
383 cout << " Path length: " << path.size() << " cities\n";
384
385 print_path_detailed(g, path);
386
387 // =========================================================================
388 // Part 2: Advanced Operations
389 // =========================================================================
390
391 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
392 cout << "Part 2: Computing Complete Shortest Paths Tree\n";
393 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
394
395 cout << "\nWhen you need shortest paths to MULTIPLE destinations, compute\n";
396 cout << "the full tree once, then query efficiently.\n\n";
397
398 // Method A: compute_min_paths_tree() - builds an actual tree graph
399 cout << "▶ Method A: compute_min_paths_tree()\n";
400 {
401 CityGraph tree;
402 double time = measure_time_ms([&]() {
403 dijkstra.compute_min_paths_tree(g, source, tree);
404 });
405
406 cout << " Tree nodes: " << tree.get_num_nodes() << "\n";
407 cout << " Tree edges: " << tree.get_num_arcs() << "\n";
408 cout << " Time: " << fixed << setprecision(3) << time << " ms\n\n";
409
410 // Query distances using the tree
411 cout << " Distances from " << source->get_info() << ":\n";
412 for (size_t i = 1; i < nodes.size(); ++i)
413 {
414 Path<CityGraph> p(g);
415 double d = dijkstra.get_min_path(tree, nodes[i], p);
416 cout << " → " << setw(8) << left << nodes[i]->get_info()
417 << right << ": " << setw(6) << d << " km\n";
418 }
419 }
420
421 // Method B: paint_min_paths_tree() - marks the graph in-place
422 cout << "\n▶ Method B: paint_min_paths_tree()\n";
423 cout << " (More memory-efficient, marks graph directly)\n";
424 {
425 double time = measure_time_ms([&]() {
426 dijkstra.paint_min_paths_tree(g, source);
427 });
428
429 cout << " Time: " << fixed << setprecision(3) << time << " ms\n";
430
431 // Query using get_min_path() after painting
432 Path<CityGraph> p(g);
433 double d = dijkstra.get_min_path(dest, p);
434 cout << " Distance to " << dest->get_info() << ": " << d << " km\n";
435 }
436
437 // =========================================================================
438 // Part 3: Performance Comparison - Binary Heap vs Fibonacci Heap
439 // =========================================================================
440
441 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
442 cout << "Part 3: Performance Comparison - Heap Types\n";
443 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
444
445 cout << "\nDijkstra can use different priority queue implementations:\n";
446 cout << " • Binary Heap (default): O((V+E) log V) - good for sparse graphs\n";
447 cout << " • Fibonacci Heap: O(E + V log V) - better for dense graphs\n\n";
448
449 // Test on larger random graphs
450 const int SPARSE_NODES = 500;
451 const double SPARSE_PROB = 0.02; // ~2% edge probability
452
453 const int DENSE_NODES = 200;
454 const double DENSE_PROB = 0.3; // ~30% edge probability
455
456 // Test sparse graph
457 cout << "▶ Sparse Graph (" << SPARSE_NODES << " nodes, ~"
458 << int(SPARSE_PROB * 100) << "% edge density):\n";
459 {
461 auto* s = sparse.get_first_node();
462
463 // Binary Heap
467 ArcHeap>;
470 double time_bin = measure_time_ms([&]() {
471 dbin.compute_min_paths_tree(sparse, s, tree_bin);
472 });
473
474 // Fibonacci Heap
481 double time_fib = measure_time_ms([&]() {
482 dfib.compute_min_paths_tree(sparse, s, tree_fib);
483 });
484
485 cout << " Binary Heap: " << fixed << setprecision(3) << time_bin << " ms\n";
486 cout << " Fibonacci Heap: " << fixed << setprecision(3) << time_fib << " ms\n";
487 cout << " Edges: " << sparse.get_num_arcs() << "\n";
488 }
489
490 // Test dense graph
491 cout << "\n▶ Dense Graph (" << DENSE_NODES << " nodes, ~"
492 << int(DENSE_PROB * 100) << "% edge density):\n";
493 {
495 auto* s = dense.get_first_node();
496
497 // Binary Heap
501 ArcHeap>;
504 double time_bin = measure_time_ms([&]() {
505 dbin.compute_min_paths_tree(dense, s, tree_bin);
506 });
507
508 // Fibonacci Heap
515 double time_fib = measure_time_ms([&]() {
516 dfib.compute_min_paths_tree(dense, s, tree_fib);
517 });
518
519 cout << " Binary Heap: " << fixed << setprecision(3) << time_bin << " ms\n";
520 cout << " Fibonacci Heap: " << fixed << setprecision(3) << time_fib << " ms\n";
521 cout << " Edges: " << dense.get_num_arcs() << "\n";
522 }
523
524 // =========================================================================
525 // Part 4: Special Cases
526 // =========================================================================
527
528 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
529 cout << "Part 4: Special Cases\n";
530 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
531
532 // Case A: Source equals destination
533 cout << "\n▶ Case A: Source = Destination (same node)\n";
534 {
535 Path<CityGraph> p(g);
536 double d = dijkstra.find_min_path(g, source, source, p);
537
538 if (d == std::numeric_limits<double>::max() && p.size() == 0)
539 {
540 cout << " Aleph-w behavior: start==end returns Inf and an empty path.\n";
541 cout << " If you want a trivial path, handle it explicitly as distance=0 and path=[start].\n";
542 }
543 else
544 {
545 cout << " Distance: " << d << " km\n";
546 cout << " Path length: " << p.size() << " cities\n";
547 }
548 }
549
550 // Case B: Unreachable destination
551 cout << "\n▶ Case B: Disconnected graph (unreachable node)\n";
552 {
554 auto* island_a = disconnected.insert_node("Island_A");
555 auto* island_b = disconnected.insert_node("Island_B");
556 disconnected.insert_node("Island_C"); // isolated
557
558 disconnected.insert_arc(island_a, island_b, 10.0);
559 disconnected.insert_arc(island_b, island_a, 10.0);
560
561 // Try to reach isolated node
563 CityGraph tree;
565
566 cout << " Graph nodes: " << disconnected.get_num_nodes() << "\n";
567 cout << " Reachable nodes (in tree): " << tree.get_num_nodes() << "\n";
568 }
569
570 // Case C: Single node graph
571 cout << "\n▶ Case C: Single-node graph\n";
572 {
574 auto* only_node = single.insert_node("Lonely");
575
577 CityGraph tree;
579
580 cout << " Tree nodes: " << tree.get_num_nodes() << "\n";
581 }
582
583 // =========================================================================
584 // Summary
585 // =========================================================================
586
587 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
588 cout << "Summary\n";
589 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
590
591 cout << R"(
592┌─────────────────────────────────────────────────────────────────────┐
593│ Dijkstra's Algorithm Properties │
594├─────────────────────────────────────────────────────────────────────┤
595│ Time Complexity: │
596│ • Binary Heap: O((V + E) log V) │
597│ • Fibonacci Heap: O(E + V log V) │
598│ │
599│ Space Complexity: O(V) │
600│ │
601│ Requirements: │
602│ • All edge weights must be non-negative │
603│ • For negative weights, use Bellman-Ford │
604├─────────────────────────────────────────────────────────────────────┤
605│ Key Methods: │
606│ • find_min_path(g, src, dst, path) - single destination │
607│ • compute_min_paths_tree(g, src, tree) - build full tree │
608│ • paint_min_paths_tree(g, src) - mark graph in-place │
609│ • get_min_path(dst, path) - query after paint │
610│ • get_min_path(tree, dst, path) - query from tree │
611│ • get_distance(dst) - just the distance after paint │
612├─────────────────────────────────────────────────────────────────────┤
613│ When to Use: │
614│ • Single shortest path: find_min_path() │
615│ • Multiple queries from same source: compute/paint tree first │
616│ • Memory-constrained: paint_min_paths_tree() │
617│ • Need actual tree structure: compute_min_paths_tree() │
618└─────────────────────────────────────────────────────────────────────┘
619)";
620
621 cout << "\nFor graphs with heuristic information, consider using A* (AStar.H)\n";
622 cout << "which can be significantly faster for single-destination queries.\n\n";
623
624 return 0;
625}
Dijkstra's shortest path algorithm.
long double w
Definition btreepic.C:153
int num_nodes
Definition btreepic.C:410
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Arc Arc
Definition graph-dry.H:3852
typename BaseGraph::Node Node
Definition graph-dry.H:3851
Spanning tree calculation of all shortest paths from a given node according to Dijkstra's algorithm.
Definition Dijkstra.H:101
GT::Node * compute_min_paths_tree(const GT &g, typename GT::Node *start, GT &tree)
Computes the spanning tree of all shortest paths from the start node.
Definition Dijkstra.H:170
Path on a graph.
Definition tpl_graph.H:2669
void for_each_node(Operation op=Operation()) const
Execute an operation on each node of path.
Definition tpl_graph.H:3331
size_t size() const noexcept
Return the path length in nodes.
Definition tpl_graph.H:2807
void for_each_arc(Operation op=Operation()) const
Execute an operation on each arc of path.
Definition tpl_graph.H:3342
List_Digraph< CityNode, RoadArc > CityGraph
Graph type: directed graph of cities connected by roads.
double measure_time_ms(Func &&f)
CityGraph build_city_graph(vector< CityGraph::Node * > &nodes)
Build a sample graph representing a city road network.
void print_graph(CityGraph &g)
Print the graph structure.
CityGraph build_random_graph(int num_nodes, double edge_probability, double max_weight, unsigned seed=42)
Build a random graph for performance testing.
void print_path_detailed(CityGraph &g, Path< CityGraph > &path)
Print a path with detailed step-by-step information.
int main()
static mt19937 rng
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::string to_string(const time_t t, const std::string &format)
Format a time_t value into a string using format.
Definition ah-date.H:140
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:121
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
Distance accessor functor for Dijkstra algorithm.
double operator()(CityGraph::Arc *arc) const
Generic graph and digraph implementations.