Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
bellman_ford_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
122#include <iostream>
123#include <iomanip>
124#include <string>
125#include <vector>
126#include <chrono>
127#include <cstring>
128
129#include <tpl_graph.H>
130#include <Bellman_Ford.H>
131
132using namespace std;
133using namespace Aleph;
134
135// =============================================================================
136// Graph Type Definitions
137// =============================================================================
138
142
145{
147
148 static void set_zero(Arc* a) { a->get_info() = 0.0; }
149
150 Distance_Type operator()(Arc* a) const { return a->get_info(); }
151};
152
153// =============================================================================
154// Utility Functions
155// =============================================================================
156
157Node* find_node(WeightedDigraph& g, const string& name)
158{
159 for (auto it = g.get_node_it(); it.has_curr(); it.next())
160 if (it.get_curr()->get_info() == name)
161 return it.get_curr();
162 return nullptr;
163}
164
166{
167 cout << "Graph structure:\n";
168 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
169 {
170 auto* node = nit.get_curr();
171 cout << " " << node->get_info() << " → ";
172 bool first = true;
173 for (auto ait = g.get_out_it(node); ait.has_curr(); ait.next())
174 {
175 auto* arc = ait.get_curr();
176 auto* tgt = g.get_tgt_node(arc);
177 if (!first) cout << ", ";
178 cout << tgt->get_info() << "(" << arc->get_info() << ")";
179 first = false;
180 }
181 cout << "\n";
182 }
183}
184
186{
187 (void)g;
188 if (path.size() == 0)
189 {
190 cout << "(no path)\n";
191 return;
192 }
193
194 double total = 0;
195 bool first = true;
196 path.for_each_node([&first](Node* n) {
197 if (!first) cout << " → ";
198 cout << n->get_info();
199 first = false;
200 });
201 cout << "\n Edges: ";
202
203 first = true;
204 path.for_each_arc([&](Arc* a) {
205 if (!first) cout << " + ";
206 cout << a->get_info();
207 total += a->get_info();
208 first = false;
209 });
210 cout << " = " << total << "\n";
211}
212
213template <typename Func>
214double measure_ms(Func&& f)
215{
216 auto start = chrono::high_resolution_clock::now();
217 f();
218 auto end = chrono::high_resolution_clock::now();
219 return chrono::duration<double, milli>(end - start).count();
220}
221
222// =============================================================================
223// Example 1: Basic Shortest Paths (with negative weights)
224// =============================================================================
225
227{
228 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
229 cout << "Example 1: Shortest Paths with Negative Weights\n";
230 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
231
232 /*
233 * Build a graph where the shortest path requires taking a negative edge:
234 *
235 * A ──(4)──→ B ──(3)──→ E
236 * │ │↗
237 * │ (-2)
238 * (2) │
239 * │ ↓
240 * └───────→ C ──(1)──→ D
241 *
242 * Shortest path A→E: A→C→B→E (cost = 2 + (-2) + 3 = 3)
243 * NOT A→B→E (cost = 4 + 3 = 7)
244 */
245
247
248 auto* A = g.insert_node("A");
249 auto* B = g.insert_node("B");
250 auto* C = g.insert_node("C");
251 auto* D = g.insert_node("D");
252 auto* E = g.insert_node("E");
253
254 g.insert_arc(A, B, 4.0);
255 g.insert_arc(A, C, 2.0);
256 g.insert_arc(B, E, 3.0);
257 g.insert_arc(C, B, -2.0); // Negative weight!
258 g.insert_arc(C, D, 1.0);
259
260 cout << "Graph with negative edge C→B (-2):\n\n";
261 print_graph(g);
262
263 cout << "\n▶ Running Bellman-Ford from A:\n\n";
264
266 bool has_negative_cycle = bf.paint_spanning_tree(A);
267
268 if (has_negative_cycle)
269 {
270 cout << " ERROR: Negative cycle detected!\n";
271 return;
272 }
273
274 cout << " No negative cycle detected.\n\n";
275
276 // Get distances and paths to all nodes
277 cout << " Distances from A:\n";
278 for (auto it = g.get_node_it(); it.has_curr(); it.next())
279 {
280 auto* node = it.get_curr();
281 double dist = bf.get_distance(node);
282 cout << " A → " << node->get_info() << ": ";
283
284 if (dist == numeric_limits<double>::max())
285 cout << "∞ (unreachable)\n";
286 else
287 cout << dist << "\n";
288 }
289
290 // Show specific path A→E
291 cout << "\n Path A → E:\n ";
292 Path<WeightedDigraph> path(g);
293 bf.get_min_path(E, path);
294 print_path(path, g);
295
296 cout << "\n Note: Dijkstra would fail here because of the negative edge!\n";
297}
298
299// =============================================================================
300// Example 2: Negative Cycle Detection
301// =============================================================================
302
304{
305 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
306 cout << "Example 2: Negative Cycle Detection\n";
307 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
308
309 /*
310 * Build a graph with a negative cycle:
311 *
312 * A ──(1)──→ B
313 * ↑ │
314 * │ (2)
315 * (-5) │
316 * │ ↓
317 * └───(1)── C
318 *
319 * Cycle B→C→A→B has weight: 2 + (-5) + 1 = -2
320 * This is a negative cycle - shortest paths are undefined!
321 */
322
324
325 auto* A = g.insert_node("A");
326 auto* B = g.insert_node("B");
327 auto* C = g.insert_node("C");
328
329 g.insert_arc(A, B, 1.0);
330 g.insert_arc(B, C, 2.0);
331 g.insert_arc(C, A, -5.0); // Creates negative cycle!
332
333 cout << "Graph with negative cycle (A→B→C→A has weight -2):\n\n";
334 print_graph(g);
335
336 cout << "\n▶ Running Bellman-Ford from A:\n\n";
337
339 bool has_negative_cycle = bf.paint_spanning_tree(A);
340
341 if (has_negative_cycle)
342 {
343 cout << " ⚠ NEGATIVE CYCLE DETECTED!\n\n";
344 cout << " When a negative cycle exists, shortest paths are undefined\n";
345 cout << " because you can always go around the cycle to decrease the cost.\n";
346
347 // Build and print the negative cycle
348 Path<WeightedDigraph> cycle = bf.test_negative_cycle();
349
350 cout << "\n Negative cycle: ";
351 cycle.for_each_node([](Node* n) {
352 cout << n->get_info() << " → ";
353 });
354 cout << "...\n";
355
356 double cycle_weight = 0;
357 cycle.for_each_arc([&cycle_weight](Arc* a) {
358 cycle_weight += a->get_info();
359 });
360 cout << " Cycle weight: " << cycle_weight << "\n";
361 }
362 else
363 {
364 cout << " No negative cycle detected.\n";
365 }
366}
367
368// =============================================================================
369// Example 3: SPFA (Faster Variant)
370// =============================================================================
371
373{
374 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
375 cout << "Example 3: Standard vs SPFA (Faster) Variant\n";
376 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
377
378 cout << "SPFA (Shortest Path Faster Algorithm) is a queue-based optimization\n";
379 cout << "of Bellman-Ford. It only relaxes edges from nodes whose distances\n";
380 cout << "have been updated, which is often much faster in practice.\n\n";
381
382 // Build a larger graph for meaningful comparison
384 const int N = 100;
385 vector<Node*> nodes;
386
387 for (int i = 0; i < N; ++i)
388 nodes.push_back(g.insert_node("N" + to_string(i)));
389
390 // Create a sparse graph with some negative edges
391 for (int i = 0; i < N; ++i)
392 {
393 // Forward edges (mostly positive)
394 if (i + 1 < N) g.insert_arc(nodes[i], nodes[i + 1], 1.0);
395 if (i + 2 < N) g.insert_arc(nodes[i], nodes[i + 2], 3.0);
396 if (i + 5 < N) g.insert_arc(nodes[i], nodes[i + 5], 4.0);
397
398 // Some negative edges (backwards, but not creating cycles)
399 if (i > 0 && i % 10 == 0)
400 g.insert_arc(nodes[i], nodes[i - 1], -0.5);
401 }
402
403 cout << "Graph: " << g.get_num_nodes() << " nodes, " << g.get_num_arcs() << " arcs\n\n";
404
405 // Standard Bellman-Ford
406 double time_standard;
407 {
409 time_standard = measure_ms([&]() {
410 bf.paint_spanning_tree(nodes[0]);
411 });
412 cout << "▶ Standard Bellman-Ford: " << fixed << setprecision(3)
413 << time_standard << " ms\n";
414 }
415
416 // SPFA variant
417 double time_spfa;
418 {
420 time_spfa = measure_ms([&]() {
421 bf.faster_paint_spanning_tree(nodes[0]);
422 });
423 cout << "▶ SPFA (Faster) variant: " << fixed << setprecision(3)
424 << time_spfa << " ms\n";
425 }
426
428 cout << "\n Speedup: " << fixed << setprecision(1) << speedup << "x\n";
429 cout << "\n Note: SPFA is usually faster on sparse graphs, but has the\n";
430 cout << " same worst-case complexity O(V*E) as standard Bellman-Ford.\n";
431}
432
433// =============================================================================
434// Example 4: Comparison with Dijkstra
435// =============================================================================
436
438{
439 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
440 cout << "Example 4: When to Use Bellman-Ford vs Dijkstra\n";
441 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
442
443 cout << R"(
444┌──────────────────────────────────────────────────────────────────────┐
445│ Algorithm Selection Guide │
446├──────────────────────────────────────────────────────────────────────┤
447│ Criterion │ Dijkstra │ Bellman-Ford │
448├────────────────────────┼─────────────────┼──────────────────────────┤
449│ Edge weights │ Non-negative │ Any (incl. negative) │
450│ Time complexity │ O((V+E) log V) │ O(V × E) │
451│ Negative cycle detect │ No │ Yes │
452│ Best for │ Road networks │ Currency exchange, │
453│ │ GPS routing │ game AI, financial │
454├────────────────────────┴─────────────────┴──────────────────────────┤
455│ Use Bellman-Ford when: │
456│ • Graph has negative edge weights │
457│ • Need to detect negative cycles │
458│ • Correctness more important than speed │
459│ │
460│ Use Dijkstra when: │
461│ • All edges are non-negative │
462│ • Performance is critical │
463│ • Working with large road/network graphs │
464└──────────────────────────────────────────────────────────────────────┘
465)";
466}
467
468// =============================================================================
469// Main
470// =============================================================================
471
472static void usage(const char* prog)
473{
474 cout << "Usage: " << prog << " [--negative-cycles] [--spfa] [--help]\n";
475 cout << "\nIf no flags are given, all demos are executed.\n";
476}
477
478static bool has_flag(int argc, char* argv[], const char* flag)
479{
480 for (int i = 1; i < argc; ++i)
481 if (std::strcmp(argv[i], flag) == 0)
482 return true;
483 return false;
484}
485
486int main(int argc, char* argv[])
487{
488 cout << "╔══════════════════════════════════════════════════════════════════════╗\n";
489 cout << "║ Bellman-Ford Algorithm - Comprehensive Example ║\n";
490 cout << "╚══════════════════════════════════════════════════════════════════════╝\n\n";
491
492 if (has_flag(argc, argv, "--help"))
493 {
494 usage(argv[0]);
495 return 0;
496 }
497
498 const bool any_specific =
499 has_flag(argc, argv, "--negative-cycles") ||
500 has_flag(argc, argv, "--spfa");
501
502 const bool run_all = (argc == 1) || !any_specific;
503
504 if (run_all)
505 {
510 }
511 else
512 {
513 // Keep the basic example as context for specialized runs.
515
516 if (has_flag(argc, argv, "--negative-cycles"))
518
519 if (has_flag(argc, argv, "--spfa"))
521 }
522
523 cout << "\nDone.\n";
524 return 0;
525}
Bellman-Ford algorithm for single-source shortest paths.
int main()
void example_comparison_dijkstra()
Node * find_node(WeightedDigraph &g, const string &name)
void print_graph(WeightedDigraph &g)
void example_basic_negative_weights()
WeightedDigraph::Node Node
static void usage(const char *prog)
static bool has_flag(int argc, char *argv[], const char *flag)
void example_spfa_comparison()
WeightedDigraph::Arc Arc
void print_path(Path< WeightedDigraph > &path, WeightedDigraph &g)
void example_negative_cycle()
double measure_ms(Func &&f)
Bellman-Ford algorithm for shortest paths with negative weights.
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
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
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
WeightedDigraph::Arc Arc
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.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Distance accessor.
static void set_zero(Arc *a)
Distance_Type operator()(Arc *a) const
Generic graph and digraph implementations.