Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
eulerian_example.C
Go to the documentation of this file.
1
151#include <iostream>
152#include <iomanip>
153#include <string>
154#include <map>
155#include <vector>
156
157#include <tclap/CmdLine.h>
158
159#include <tpl_graph.H>
160#include <eulerian.H>
161
162using namespace std;
163using namespace Aleph;
164
165// Graph types
170
171// =============================================================================
172// Helper functions
173// =============================================================================
174
175void print_section(const string& title)
176{
177 cout << "\n" << string(60, '=') << "\n";
178 cout << " " << title << "\n";
179 cout << string(60, '=') << "\n\n";
180}
181
182void print_subsection(const string& title)
183{
184 cout << "\n--- " << title << " ---\n";
185}
186
187template <typename G>
188void print_graph(const string& label, G& g)
189{
190 cout << label << ":" << endl;
191 cout << " Nodes: " << g.get_num_nodes() << endl;
192 cout << " Arcs: " << g.get_num_arcs() << endl;
193
194 cout << " Vertices:" << endl;
195 for (auto it = g.get_node_it(); it.has_curr(); it.next())
196 {
197 auto node = it.get_curr();
198 cout << " " << node->get_info();
199
200 // Count degree
201 size_t degree = 0;
202 for (Node_Arc_Iterator<G> ait(node); ait.has_curr(); ait.next())
203 degree++;
204
205 cout << " (degree=" << degree << ")" << endl;
206 }
207
208 cout << " Edges:" << endl;
209 for (auto it = g.get_arc_it(); it.has_curr(); it.next())
210 {
211 auto arc = it.get_curr();
212 cout << " " << g.get_src_node(arc)->get_info()
213 << " -- " << g.get_tgt_node(arc)->get_info() << endl;
214 }
215}
216
217// =============================================================================
218// 1. Eulerian Cycle Detection
219// =============================================================================
220
222{
223 print_section("EULERIAN CYCLE DETECTION");
224
225 cout << "An Eulerian CYCLE visits every edge exactly once and returns to start.\n";
226 cout << "Condition (undirected): ALL vertices must have EVEN degree.\n\n";
227
228 // Graph 1: Triangle (Eulerian)
229 print_subsection("Example 1: Triangle (Eulerian)");
230
231 UGraph triangle;
232 auto a = triangle.insert_node("A");
233 auto b = triangle.insert_node("B");
234 auto c = triangle.insert_node("C");
235 triangle.insert_arc(a, b, 1);
236 triangle.insert_arc(b, c, 1);
237 triangle.insert_arc(c, a, 1);
238
239 print_graph("Triangle", triangle);
240 cout << "\nAll vertices have degree 2 (even)." << endl;
241
243 cout << "Is Eulerian? " << (test1(triangle) ? "YES" : "NO") << endl;
244 cout << "Eulerian cycle: A -> B -> C -> A" << endl;
245
246 // Graph 2: Square with diagonals (Eulerian)
247 print_subsection("Example 2: Square with diagonals (Eulerian)");
248
249 UGraph square;
250 auto s1 = square.insert_node("1");
251 auto s2 = square.insert_node("2");
252 auto s3 = square.insert_node("3");
253 auto s4 = square.insert_node("4");
254 // Square edges
255 square.insert_arc(s1, s2, 1);
256 square.insert_arc(s2, s3, 1);
257 square.insert_arc(s3, s4, 1);
258 square.insert_arc(s4, s1, 1);
259 // Diagonals
260 square.insert_arc(s1, s3, 1);
261 square.insert_arc(s2, s4, 1);
262
263 print_graph("Square with diagonals", square);
264 cout << "\nAll vertices have degree 4 (even)." << endl;
265
267 cout << "Is Eulerian? " << (test2(square) ? "YES" : "NO") << endl;
268
269 // Graph 3: Path (NOT Eulerian)
270 print_subsection("Example 3: Simple Path (NOT Eulerian)");
271
272 UGraph path;
273 auto p1 = path.insert_node("X");
274 auto p2 = path.insert_node("Y");
275 auto p3 = path.insert_node("Z");
276 path.insert_arc(p1, p2, 1);
277 path.insert_arc(p2, p3, 1);
278
279 print_graph("Simple path", path);
280 cout << "\nX has degree 1 (odd), Z has degree 1 (odd)." << endl;
281
283 cout << "Is Eulerian? " << (test3(path) ? "YES" : "NO") << endl;
284 cout << "Cannot return to start without reusing edges." << endl;
285}
286
287// =============================================================================
288// 2. The Seven Bridges of Königsberg
289// =============================================================================
290
292{
293 print_section("THE SEVEN BRIDGES OF KÖNIGSBERG");
294
295 cout << "The famous problem that started graph theory (Euler, 1736).\n\n";
296 cout << "Can you cross all 7 bridges exactly once and return to start?\n\n";
297
298 cout << "The city of Königsberg (now Kaliningrad) had:\n";
299 cout << " - 4 land masses (A, B, C, D)\n";
300 cout << " - 7 bridges connecting them\n\n";
301
302 /*
303 * A
304 * /|\
305 * / | \
306 * / | \ (2 bridges A-B, 2 bridges A-C)
307 * B---+---C (1 bridge A-D, 1 bridge B-D, 1 bridge C-D)
308 * \ | /
309 * \ | /
310 * \|/
311 * D
312 */
313
315 auto kA = konigsberg.insert_node("A"); // North bank
316 auto kB = konigsberg.insert_node("B"); // West island
317 auto kC = konigsberg.insert_node("C"); // East island
318 auto kD = konigsberg.insert_node("D"); // South bank
319
320 // 7 bridges
321 konigsberg.insert_arc(kA, kB, 1); // Bridge 1: A-B
322 konigsberg.insert_arc(kA, kB, 2); // Bridge 2: A-B (second)
323 konigsberg.insert_arc(kA, kC, 1); // Bridge 3: A-C
324 konigsberg.insert_arc(kA, kC, 2); // Bridge 4: A-C (second)
325 konigsberg.insert_arc(kA, kD, 1); // Bridge 5: A-D
326 konigsberg.insert_arc(kB, kD, 1); // Bridge 6: B-D
327 konigsberg.insert_arc(kC, kD, 1); // Bridge 7: C-D
328
329 cout << "Graph representation:" << endl;
330 cout << " Vertices (land masses): A, B, C, D" << endl;
331 cout << " Edges (bridges): 7" << endl;
332 cout << "\nDegrees:" << endl;
333 cout << " A: degree 5 (ODD)" << endl;
334 cout << " B: degree 3 (ODD)" << endl;
335 cout << " C: degree 3 (ODD)" << endl;
336 cout << " D: degree 3 (ODD)" << endl;
337
339 cout << "\nIs Eulerian (can return to start)? " << (test(konigsberg) ? "YES" : "NO") << endl;
340
341 cout << "\nEuler proved: With 4 odd-degree vertices, it's IMPOSSIBLE!\n";
342 cout << "For an Eulerian cycle, ALL vertices must have even degree.\n";
343 cout << "For an Eulerian path, exactly 0 or 2 vertices can have odd degree.\n";
344}
345
346// =============================================================================
347// 3. Directed Graph Eulerian
348// =============================================================================
349
351{
352 print_section("DIRECTED GRAPH EULERIAN");
353
354 cout << "For directed graphs, degree balance alone is not enough for an Eulerian cycle.\n";
355 cout << "Aleph-w checks in-degree/out-degree balance and also performs a reachability\n";
356 cout << "check among non-isolated vertices for cycle classification.\n\n";
357
358 // Eulerian directed graph
359 print_subsection("Example 1: Directed cycle (Eulerian)");
360
362 auto d1 = dcycle.insert_node("1");
363 auto d2 = dcycle.insert_node("2");
364 auto d3 = dcycle.insert_node("3");
365 dcycle.insert_arc(d1, d2, 1);
366 dcycle.insert_arc(d2, d3, 1);
367 dcycle.insert_arc(d3, d1, 1);
368
369 cout << "Directed cycle: 1 -> 2 -> 3 -> 1" << endl;
370 cout << " Node 1: in=1, out=1" << endl;
371 cout << " Node 2: in=1, out=1" << endl;
372 cout << " Node 3: in=1, out=1" << endl;
373
375 cout << "\nIs Eulerian? " << (dtest1(dcycle) ? "YES" : "NO") << endl;
376
377 // Non-Eulerian directed graph
378 print_subsection("Example 2: Directed path (NOT Eulerian)");
379
381 auto dp1 = dpath.insert_node("A");
382 auto dp2 = dpath.insert_node("B");
383 auto dp3 = dpath.insert_node("C");
384 dpath.insert_arc(dp1, dp2, 1);
385 dpath.insert_arc(dp2, dp3, 1);
386
387 cout << "Directed path: A -> B -> C" << endl;
388 cout << " Node A: in=0, out=1 (UNBALANCED)" << endl;
389 cout << " Node B: in=1, out=1" << endl;
390 cout << " Node C: in=1, out=0 (UNBALANCED)" << endl;
391
393 cout << "\nIs Eulerian? " << (dtest2(dpath) ? "YES" : "NO") << endl;
394
395 // Complex Eulerian digraph
396 print_subsection("Example 3: Figure-8 (Eulerian)");
397
398 DGraph fig8;
399 auto f1 = fig8.insert_node("Center");
400 auto f2 = fig8.insert_node("Top");
401 auto f3 = fig8.insert_node("Bottom");
402 // Upper loop
403 fig8.insert_arc(f1, f2, 1);
404 fig8.insert_arc(f2, f1, 1);
405 // Lower loop
406 fig8.insert_arc(f1, f3, 1);
407 fig8.insert_arc(f3, f1, 1);
408
409 cout << "Figure-8 shape:" << endl;
410 cout << " Center: in=2, out=2" << endl;
411 cout << " Top: in=1, out=1" << endl;
412 cout << " Bottom: in=1, out=1" << endl;
413
415 cout << "\nIs Eulerian? " << (dtest3(fig8) ? "YES" : "NO") << endl;
416}
417
418// =============================================================================
419// 4. Practical Applications
420// =============================================================================
421
423{
424 print_section("PRACTICAL APPLICATIONS");
425
426 // 1. Chinese Postman Problem (simplified)
427 print_subsection("Application 1: Mail Delivery Route");
428
429 cout << "A mail carrier wants to visit every street exactly once.\n";
430 cout << "This is the Eulerian path/cycle problem!\n\n";
431
433 auto h1 = streets.insert_node("Casa1");
434 auto h2 = streets.insert_node("Casa2");
435 auto h3 = streets.insert_node("Casa3");
436 auto h4 = streets.insert_node("Casa4");
437 auto office = streets.insert_node("Correo");
438
439 // Create a grid-like structure
440 streets.insert_arc(office, h1, 1);
441 streets.insert_arc(office, h2, 1);
442 streets.insert_arc(h1, h2, 1);
443 streets.insert_arc(h1, h3, 1);
444 streets.insert_arc(h2, h4, 1);
445 streets.insert_arc(h3, h4, 1);
446 streets.insert_arc(h3, office, 1);
447 streets.insert_arc(h4, office, 1);
448
449 print_graph("Mail route", streets);
450
452 if (mail_test(streets))
453 cout << "\nPerfect! The mail carrier can visit every street exactly once\n"
454 << "and return to the post office!\n";
455 else
456 cout << "\nSome streets must be visited more than once.\n";
457
458 // 2. Circuit Board Design
459 print_subsection("Application 2: Circuit Board Routing");
460
461 cout << "Draw all connections without lifting the pen?\n";
462 cout << "This is an Eulerian path problem!\n\n";
463
465 auto pin1 = circuit.insert_node("Pin1");
466 auto pin2 = circuit.insert_node("Pin2");
467 auto pin3 = circuit.insert_node("Pin3");
468 auto pin4 = circuit.insert_node("Pin4");
469
470 // Non-Eulerian connections
471 circuit.insert_arc(pin1, pin2, 1);
472 circuit.insert_arc(pin2, pin3, 1);
473 circuit.insert_arc(pin3, pin4, 1);
474 circuit.insert_arc(pin4, pin1, 1);
475 circuit.insert_arc(pin1, pin3, 1); // Diagonal
476
477 cout << "Circuit with 5 connections:" << endl;
478 cout << " Pin1-Pin2, Pin2-Pin3, Pin3-Pin4, Pin4-Pin1, Pin1-Pin3" << endl;
479
480 // Count odd-degree vertices
481 size_t odd_count = 0;
482 for (auto it = circuit.get_node_it(); it.has_curr(); it.next())
483 {
484 size_t degree = 0;
485 for (Node_Arc_Iterator<UGraph> ait(it.get_curr()); ait.has_curr(); ait.next())
486 degree++;
487 if (degree % 2 == 1) odd_count++;
488 }
489
490 cout << "Vertices with odd degree: " << odd_count << endl;
491
492 if (odd_count == 0)
493 cout << "Can draw all connections returning to start (Eulerian cycle)!\n";
494 else if (odd_count == 2)
495 cout << "Can draw all connections but not return to start (Eulerian path).\n";
496 else
497 cout << "Cannot draw without lifting pen - need " << (odd_count/2) << " extra strokes.\n";
498}
499
500// =============================================================================
501// 5. Finding Eulerian Paths with Hierholzer's Algorithm
502// =============================================================================
503
505{
506 print_section("HIERHOLZER'S ALGORITHM: Finding Eulerian Paths");
507
508 cout << "Hierholzer's algorithm constructs an Eulerian path/cycle in O(E) time.\n";
509 cout << "Instead of just testing existence, it finds the actual path!\n\n";
510
511 // Triangle - Eulerian cycle
512 print_subsection("Example 1: Triangle (find the cycle)");
513
514 UGraph triangle;
515 auto a = triangle.insert_node("A");
516 auto b = triangle.insert_node("B");
517 auto c = triangle.insert_node("C");
518 triangle.insert_arc(a, b, 1);
519 triangle.insert_arc(b, c, 1);
520 triangle.insert_arc(c, a, 1);
521
523 auto result1 = finder1(triangle);
524
525 cout << "Triangle graph: A-B-C\n";
526 cout << "Classification: ";
527 switch (result1.type) {
528 case Eulerian_Type::Cycle: cout << "EULERIAN CYCLE\n"; break;
529 case Eulerian_Type::Path: cout << "EULERIAN PATH\n"; break;
530 case Eulerian_Type::None: cout << "NOT EULERIAN\n"; break;
531 }
532
533 cout << "Path found (" << result1.path.size() << " edges):\n ";
534
535 auto nodes1 = finder1.find_node_sequence(triangle);
536 bool first = true;
537 for (auto node : nodes1) {
538 if (!first) cout << " -> ";
539 cout << node->get_info();
540 first = false;
541 }
542 cout << endl;
543
544 // Path graph - Eulerian path (not cycle)
545 print_subsection("Example 2: Path graph (Eulerian path, not cycle)");
546
547 UGraph path;
548 auto p1 = path.insert_node("1");
549 auto p2 = path.insert_node("2");
550 auto p3 = path.insert_node("3");
551 auto p4 = path.insert_node("4");
552 path.insert_arc(p1, p2, 1);
553 path.insert_arc(p2, p3, 1);
554 path.insert_arc(p3, p4, 1);
555
557 auto result2 = finder2(path);
558
559 cout << "Linear path: 1-2-3-4\n";
560 cout << "Classification: ";
561 switch (result2.type) {
562 case Eulerian_Type::Cycle: cout << "EULERIAN CYCLE\n"; break;
563 case Eulerian_Type::Path: cout << "EULERIAN PATH\n"; break;
564 case Eulerian_Type::None: cout << "NOT EULERIAN\n"; break;
565 }
566
567 if (result2.type != Eulerian_Type::None) {
568 cout << "Path found (" << result2.path.size() << " edges):\n ";
569 auto nodes2 = finder2.find_node_sequence(path);
570 first = true;
571 for (auto node : nodes2) {
572 if (!first) cout << " -> ";
573 cout << node->get_info();
574 first = false;
575 }
576 cout << endl;
577 }
578
579 // Bow-tie graph - complex Eulerian cycle
580 print_subsection("Example 3: Bow-tie graph (two triangles sharing a vertex)");
581
583 auto center = bowtie.insert_node("Center");
584 auto top1 = bowtie.insert_node("Top1");
585 auto top2 = bowtie.insert_node("Top2");
586 auto bot1 = bowtie.insert_node("Bot1");
587 auto bot2 = bowtie.insert_node("Bot2");
588
589 // Upper triangle
590 bowtie.insert_arc(center, top1, 1);
591 bowtie.insert_arc(top1, top2, 1);
592 bowtie.insert_arc(top2, center, 1);
593 // Lower triangle
594 bowtie.insert_arc(center, bot1, 1);
595 bowtie.insert_arc(bot1, bot2, 1);
596 bowtie.insert_arc(bot2, center, 1);
597
599 auto result3 = finder3(bowtie);
600
601 cout << "Bow-tie: Two triangles sharing 'Center'\n";
602 cout << " Center has degree 4 (even)\n";
603 cout << " All others have degree 2 (even)\n";
604 cout << "Classification: ";
605 switch (result3.type) {
606 case Eulerian_Type::Cycle: cout << "EULERIAN CYCLE\n"; break;
607 case Eulerian_Type::Path: cout << "EULERIAN PATH\n"; break;
608 case Eulerian_Type::None: cout << "NOT EULERIAN\n"; break;
609 }
610
611 cout << "Path found (" << result3.path.size() << " edges):\n ";
612 auto nodes3 = finder3.find_node_sequence(bowtie);
613 first = true;
614 for (auto node : nodes3) {
615 if (!first) cout << " -> ";
616 cout << node->get_info();
617 first = false;
618 }
619 cout << endl;
620
621 cout << "\nHierholzer's algorithm visits both triangles, returning to start!\n";
622}
623
624// =============================================================================
625// 6. Using Eulerian_Type enum
626// =============================================================================
627
629{
630 print_section("EULERIAN CLASSIFICATION WITH compute()");
631
632 cout << "The compute() method returns detailed classification:\n";
633 cout << " - Eulerian_Type::Cycle - Has Eulerian cycle\n";
634 cout << " - Eulerian_Type::Path - Has Eulerian path but not cycle\n";
635 cout << " - Eulerian_Type::None - Not Eulerian\n\n";
636
637 // Test various graphs
638 struct TestCase {
639 string name;
640 UGraph g;
642 };
643
645
646 // Triangle (cycle)
647 TestCase t1{"Triangle", {}, {{"A","B"},{"B","C"},{"C","A"}}};
648 // Path (path only)
649 TestCase t2{"Path 1-2-3", {}, {{"1","2"},{"2","3"}}};
650 // Star (none)
651 TestCase t3{"Star", {}, {{"C","1"},{"C","2"},{"C","3"},{"C","4"}}};
652
653 tests.push_back(t1);
654 tests.push_back(t2);
655 tests.push_back(t3);
656
657 cout << setw(20) << "Graph" << setw(15) << "Result" << setw(20) << "has_eulerian_path()" << endl;
658 cout << string(55, '-') << endl;
659
660 for (auto& tc : tests) {
661 // Build graph
663 for (auto& [u, v] : tc.edges) {
664 if (node_map.find(u) == node_map.end())
665 node_map[u] = tc.g.insert_node(u);
666 if (node_map.find(v) == node_map.end())
667 node_map[v] = tc.g.insert_node(v);
668 tc.g.insert_arc(node_map[u], node_map[v], 1);
669 }
670
672 auto result = tester.compute(tc.g);
673
674 string result_str;
675 switch (result) {
676 case Eulerian_Type::Cycle: result_str = "CYCLE"; break;
677 case Eulerian_Type::Path: result_str = "PATH"; break;
678 case Eulerian_Type::None: result_str = "NONE"; break;
679 }
680
681 cout << setw(20) << tc.name
682 << setw(15) << result_str
683 << setw(20) << (tester.has_eulerian_path(tc.g) ? "true" : "false") << endl;
684 }
685}
686
687// =============================================================================
688// Main
689// =============================================================================
690
691int main(int argc, char* argv[])
692{
693 try
694 {
695 TCLAP::CmdLine cmd(
696 "Eulerian graph example for Aleph-w.\n"
697 "Demonstrates Eulerian path and cycle detection.",
698 ' ', "1.0"
699 );
700
701 TCLAP::ValueArg<string> sectionArg(
702 "s", "section",
703 "Run specific section: cycle, konigsberg, directed, practical, hierholzer, types, or 'all'",
704 false, "all", "section", cmd
705 );
706
707 cmd.parse(argc, argv);
708
709 string section = sectionArg.getValue();
710
711 cout << "\n";
712 cout << "============================================================\n";
713 cout << " ALEPH-W EULERIAN GRAPHS EXAMPLE\n";
714 cout << "============================================================\n";
715
716 if (section == "all" or section == "cycle")
718
719 if (section == "all" or section == "konigsberg")
721
722 if (section == "all" or section == "directed")
724
725 if (section == "all" or section == "practical")
727
728 if (section == "all" or section == "hierholzer")
730
731 if (section == "all" or section == "types")
733
734 cout << "\n" << string(60, '=') << "\n";
735 cout << "Eulerian graphs demo completed!\n";
736 cout << string(60, '=') << "\n\n";
737
738 return 0;
739 }
740 catch (TCLAP::ArgException& e)
741 {
742 cerr << "Error: " << e.error() << " for argument " << e.argId() << endl;
743 return 1;
744 }
745 catch (exception& e)
746 {
747 cerr << "Error: " << e.what() << endl;
748 return 1;
749 }
750}
751
int main()
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
Finds and constructs an Eulerian path or cycle using Hierholzer's algorithm.
Definition eulerian.H:561
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:428
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Tests whether a graph or digraph is Eulerian.
Definition eulerian.H:171
Eulerian graph detection and path/cycle finding.
void print_subsection(const string &title)
void demo_eulerian_type()
void demo_konigsberg()
void print_section(const string &title)
void demo_hierholzer()
void demo_directed()
void demo_practical()
void print_graph(const string &label, G &g)
void demo_eulerian_cycle()
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
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
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
void test(unsigned long n, gsl_rng *r)
Generic graph and digraph implementations.