Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
hamiltonian_example.C
Go to the documentation of this file.
1
198#include <iostream>
199#include <iomanip>
200#include <string>
201
202#include <tclap/CmdLine.h>
203
204#include <tpl_graph.H>
205#include <hamiltonian.H>
206
207using namespace std;
208using namespace Aleph;
209
210// Graph types
214
215// =============================================================================
216// Helper functions
217// =============================================================================
218
219void print_section(const string& title)
220{
221 cout << "\n" << string(60, '=') << "\n";
222 cout << " " << title << "\n";
223 cout << string(60, '=') << "\n\n";
224}
225
226void print_subsection(const string& title)
227{
228 cout << "\n--- " << title << " ---\n";
229}
230
232{
233 cout << "Vertex degrees:" << endl;
234 for (auto it = g.get_node_it(); it.has_curr(); it.next())
235 {
236 auto node = it.get_curr();
237 size_t degree = 0;
238 for (Node_Arc_Iterator<UGraph> ait(node); ait.has_curr(); ait.next())
239 degree++;
240 cout << " " << node->get_info() << ": degree " << degree << endl;
241 }
242}
243
244// Build complete graph Kn
245void build_complete_graph(UGraph& g, size_t n)
246{
248
249 // Create nodes
250 for (size_t i = 0; i < n; i++)
251 nodes.push_back(g.insert_node(to_string(i)));
252
253 // Connect all pairs
254 for (size_t i = 0; i < n; i++)
255 for (size_t j = i + 1; j < n; j++)
256 g.insert_arc(nodes[i], nodes[j], 1);
257}
258
259// =============================================================================
260// 1. Hamiltonian vs Eulerian
261// =============================================================================
262
264{
265 print_section("HAMILTONIAN VS EULERIAN");
266
267 cout << "Hamiltonian: Visit every VERTEX exactly once\n";
268 cout << "Eulerian: Visit every EDGE exactly once\n\n";
269
270 // Example: Triangle
271 print_subsection("Example: Triangle (K3)");
272
273 UGraph triangle;
274 auto a = triangle.insert_node("A");
275 auto b = triangle.insert_node("B");
276 auto c = triangle.insert_node("C");
277 triangle.insert_arc(a, b, 1);
278 triangle.insert_arc(b, c, 1);
279 triangle.insert_arc(c, a, 1);
280
281 cout << "Triangle: A-B-C-A\n";
282 cout << " Vertices: 3, Edges: 3\n";
283 cout << " Each vertex has degree 2\n\n";
284
285 cout << "Hamiltonian cycle: A -> B -> C -> A (visits each vertex once)\n";
286 cout << "Eulerian cycle: A -> B -> C -> A (visits each edge once)\n";
287 cout << "\nTriangle is BOTH Hamiltonian AND Eulerian!\n";
288
289 // Example: Star graph
290 print_subsection("Example: Star graph (K1,4)");
291
292 UGraph star;
293 auto center = star.insert_node("Center");
294 auto p1 = star.insert_node("P1");
295 auto p2 = star.insert_node("P2");
296 auto p3 = star.insert_node("P3");
297 auto p4 = star.insert_node("P4");
298 star.insert_arc(center, p1, 1);
299 star.insert_arc(center, p2, 1);
300 star.insert_arc(center, p3, 1);
301 star.insert_arc(center, p4, 1);
302
303 cout << "Star: Center connected to P1, P2, P3, P4\n";
304 cout << " Center degree: 4\n";
305 cout << " P1-P4 degrees: 1 each\n\n";
306
307 cout << "Hamiltonian? NO - Can't visit all without repeating Center\n";
308 cout << "Eulerian? NO - Peripheral vertices have odd degree\n";
309}
310
311// =============================================================================
312// 2. Ore's Theorem
313// =============================================================================
314
316{
317 print_section("ORE'S THEOREM (Sufficiency Condition)");
318
319 cout << "Ore's Theorem states:\n";
320 cout << "For a graph G with n >= 3 vertices, if for every pair of\n";
321 cout << "NON-ADJACENT vertices u, v: deg(u) + deg(v) >= n,\n";
322 cout << "then G has a Hamiltonian cycle.\n\n";
323
324 // Complete graph K5 - satisfies Ore's condition
325 print_subsection("Example 1: Complete graph K5");
326
327 UGraph k5;
329
330 cout << "K5: Complete graph with 5 vertices\n";
331 cout << " All vertices connected to all others\n";
332 cout << " Each vertex has degree 4\n\n";
333
334 cout << "Check Ore's condition:\n";
335 cout << " In K5, every pair IS adjacent (no non-adjacent pairs)\n";
336 cout << " Condition is trivially satisfied!\n\n";
337
339 cout << "Satisfies Ore's condition? " << (test1(k5) ? "YES" : "NO") << endl;
340 cout << "=> K5 is Hamiltonian\n";
341
342 // Cycle graph C5 - does NOT satisfy Ore's but IS Hamiltonian
343 print_subsection("Example 2: Cycle C5 (Pentagon)");
344
345 UGraph c5;
346 auto n0 = c5.insert_node("0");
347 auto n1 = c5.insert_node("1");
348 auto n2 = c5.insert_node("2");
349 auto n3 = c5.insert_node("3");
350 auto n4 = c5.insert_node("4");
351 c5.insert_arc(n0, n1, 1);
352 c5.insert_arc(n1, n2, 1);
353 c5.insert_arc(n2, n3, 1);
354 c5.insert_arc(n3, n4, 1);
355 c5.insert_arc(n4, n0, 1);
356
357 cout << "C5: Cycle 0-1-2-3-4-0\n";
358 cout << " Each vertex has degree 2\n\n";
359
360 cout << "Check Ore's condition:\n";
361 cout << " Non-adjacent pair (0, 2): deg(0) + deg(2) = 2 + 2 = 4\n";
362 cout << " Need >= n = 5, but only have 4\n";
363 cout << " Condition NOT satisfied!\n\n";
364
366 cout << "Satisfies Ore's condition? " << (test2(c5) ? "YES" : "NO") << endl;
367 cout << "\nBUT: C5 IS Hamiltonian! (The cycle itself is Hamiltonian)\n";
368 cout << "=> Ore's is SUFFICIENT but not NECESSARY\n";
369}
370
371// =============================================================================
372// 3. Practical Examples
373// =============================================================================
374
376{
377 print_section("PRACTICAL: Traveling Salesman Setup");
378
379 cout << "The Hamiltonian cycle problem is the foundation of TSP.\n";
380 cout << "TSP asks: What's the shortest Hamiltonian cycle?\n\n";
381
382 // Colombian cities as vertices
383 print_subsection("Colombian cities tour");
384
386 auto bog = colombia.insert_node("Bogota");
387 auto med = colombia.insert_node("Medellin");
388 auto cal = colombia.insert_node("Cali");
389 auto bar = colombia.insert_node("Barranquilla");
390 auto car = colombia.insert_node("Cartagena");
391
392 // Create some connections (not all cities directly connected)
393 colombia.insert_arc(bog, med, 1); // Bogota-Medellin
394 colombia.insert_arc(bog, cal, 1); // Bogota-Cali
395 colombia.insert_arc(med, cal, 1); // Medellin-Cali
396 colombia.insert_arc(med, bar, 1); // Medellin-Barranquilla
397 colombia.insert_arc(bar, car, 1); // Barranquilla-Cartagena
398 colombia.insert_arc(bog, bar, 1); // Bogota-Barranquilla
399
400 cout << "Cities: Bogota, Medellin, Cali, Barranquilla, Cartagena\n";
401 cout << "Connections:\n";
402 cout << " Bogota-Medellin, Bogota-Cali, Bogota-Barranquilla\n";
403 cout << " Medellin-Cali, Medellin-Barranquilla\n";
404 cout << " Barranquilla-Cartagena\n\n";
405
407
408 cout << "\nNon-adjacent pairs check:\n";
409 cout << " (Bogota, Cartagena): 3 + 1 = 4 < 5 - FAILS\n";
410 cout << " (Cali, Barranquilla): 2 + 2 = 4 < 5 - FAILS\n";
411 cout << " (Cali, Cartagena): 2 + 1 = 3 < 5 - FAILS\n";
412
414 cout << "\nSatisfies Ore's condition? " << (test(colombia) ? "YES" : "NO") << endl;
415
416 cout << "\nThis doesn't mean no Hamiltonian cycle exists!\n";
417 cout << "Let's check manually:\n";
418 cout << " Bogota -> Medellin -> Barranquilla -> Cartagena -> ?\n";
419 cout << " Cartagena only connects to Barranquilla - STUCK!\n";
420 cout << "\nNeed to add Cali-Cartagena or Bogota-Cartagena connection.\n";
421
422 // Add missing connection
423 print_subsection("Adding Cali-Cartagena connection");
424
425 colombia.insert_arc(cal, car, 1);
426
427 cout << "Added: Cali-Cartagena\n\n";
429
430 cout << "\nNow we can find a Hamiltonian cycle:\n";
431 cout << " Bogota -> Cali -> Cartagena -> Barranquilla -> Medellin -> Bogota\n";
432 cout << " (Visits each city exactly once and returns to start)\n";
433}
434
435// =============================================================================
436// 4. Dense vs Sparse Graphs
437// =============================================================================
438
440{
441 print_section("GRAPH DENSITY AND HAMILTONICITY");
442
443 cout << "Dense graphs are more likely to satisfy Ore's condition.\n\n";
444
445 // Compare different densities
447 {"Sparse (n edges)", 8, 8},
448 {"Medium (2n edges)", 8, 16},
449 {"Dense (3n edges)", 8, 24},
450 {"Complete (n(n-1)/2)", 8, 28}
451 };
452
453 cout << setw(25) << "Configuration" << setw(15) << "Satisfies Ore?" << endl;
454 cout << string(40, '-') << endl;
455
456 for (auto& [name, n, target_edges] : configs)
457 {
458 UGraph g;
460
461 // Create nodes
462 for (size_t i = 0; i < n; i++)
463 nodes.push_back(g.insert_node(to_string(i)));
464
465 // Add edges up to target
466 size_t edges = 0;
467 for (size_t i = 0; i < n and edges < target_edges; i++)
468 {
469 for (size_t j = i + 1; j < n and edges < target_edges; j++)
470 {
471 g.insert_arc(nodes[i], nodes[j], 1);
472 edges++;
473 }
474 }
475
477 cout << setw(25) << name << setw(15) << (test(g) ? "YES" : "NO") << endl;
478 }
479
480 cout << "\nConclusion: Denser graphs more likely to be Hamiltonian.\n";
481}
482
483// =============================================================================
484// 5. Dirac's Theorem (Faster Alternative)
485// =============================================================================
486
488{
489 print_section("DIRAC'S THEOREM (O(V) Alternative to Ore)");
490
491 cout << "Dirac's Theorem:\n";
492 cout << "For a graph G with n >= 3 vertices, if deg(v) >= n/2 for ALL vertices,\n";
493 cout << "then G has a Hamiltonian cycle.\n\n";
494
495 cout << "Comparison with Ore's Theorem:\n";
496 cout << " | Property | Dirac | Ore |\n";
497 cout << " |--------------|-------------|-------------|\n";
498 cout << " | Complexity | O(V) | O(V^2) |\n";
499 cout << " | Condition | deg >= n/2 | sum >= n |\n";
500 cout << " | Strictness | More strict | Less strict |\n\n";
501
502 // Complete graph K6 - satisfies both
503 print_subsection("Example 1: Complete graph K6");
504
505 UGraph k6;
507
508 cout << "K6: Complete graph with 6 vertices\n";
509 cout << " Each vertex has degree 5\n";
510 cout << " n/2 = 3, so need deg >= 3\n";
511 cout << " 5 >= 3 ✓\n\n";
512
515
516 cout << "Dirac: " << (dirac1(k6) ? "SATISFIED" : "NOT satisfied") << endl;
517 cout << "Ore: " << (ore1(k6) ? "SATISFIED" : "NOT satisfied") << endl;
518 cout << "Min required degree: " << dirac1.min_required_degree(k6) << endl;
519
520 // Cycle C5 - fails Dirac but may satisfy Ore in some cases
521 print_subsection("Example 2: Cycle C6");
522
523 UGraph c6;
525 for (int i = 0; i < 6; i++)
526 cycle_nodes.push_back(c6.insert_node(to_string(i)));
527 for (int i = 0; i < 6; i++)
528 c6.insert_arc(cycle_nodes[i], cycle_nodes[(i+1) % 6], 1);
529
530 cout << "C6: Cycle 0-1-2-3-4-5-0\n";
531 cout << " Each vertex has degree 2\n";
532 cout << " n/2 = 3, need deg >= 3\n";
533 cout << " 2 < 3 ✗\n\n";
534
537
538 cout << "Dirac: " << (dirac2(c6) ? "SATISFIED" : "NOT satisfied") << endl;
539 cout << "Ore: " << (ore2(c6) ? "SATISFIED" : "NOT satisfied") << endl;
540
541 auto [min_deg, min_node] = dirac2.find_min_degree_vertex(c6);
542 cout << "Min degree found: " << min_deg << " (at vertex " << min_node->get_info() << ")\n";
543 cout << "\nBUT: C6 IS Hamiltonian! (The cycle itself is the Hamiltonian cycle)\n";
544
545 // Near-complete graph - satisfies Dirac
546 print_subsection("Example 3: Dense graph (nearly complete)");
547
550 const size_t n = 8;
551 for (size_t i = 0; i < n; i++)
552 dense_nodes.push_back(dense.insert_node(to_string(i)));
553
554 // Connect each vertex to at least n/2 others
555 for (size_t i = 0; i < n; i++)
556 for (size_t j = i + 1; j < n; j++)
557 if (j - i <= n/2 or i + n - j <= n/2) // Connect nearby vertices
558 dense.insert_arc(dense_nodes[i], dense_nodes[j], 1);
559
561
563 cout << "\nMin required degree (n/2): " << dirac3.min_required_degree(dense) << endl;
564 cout << "Dirac: " << (dirac3(dense) ? "SATISFIED" : "NOT satisfied") << endl;
565
566 // Speed comparison
567 print_subsection("Why use Dirac over Ore?");
568
569 cout << "For large graphs:\n";
570 cout << " - Ore checks ALL pairs of non-adjacent vertices: O(V^2)\n";
571 cout << " - Dirac checks EACH vertex's degree: O(V)\n\n";
572
573 cout << "Example timing for n=1000:\n";
574 cout << " Ore: ~1,000,000 pair comparisons\n";
575 cout << " Dirac: ~1,000 degree checks\n\n";
576
577 cout << "Use Dirac when:\n";
578 cout << " - Graph is expected to be dense\n";
579 cout << " - Quick preliminary test needed\n";
580 cout << " - Performance is critical\n\n";
581
582 cout << "Note: If Dirac passes, Ore also passes (but not vice versa).\n";
583}
584
585// =============================================================================
586// Main
587// =============================================================================
588
589int main(int argc, char* argv[])
590{
591 try
592 {
593 TCLAP::CmdLine cmd(
594 "Hamiltonian graph example for Aleph-w.\n"
595 "Demonstrates Ore's sufficiency condition for Hamiltonian cycles.",
596 ' ', "1.0"
597 );
598
599 TCLAP::ValueArg<string> sectionArg(
600 "s", "section",
601 "Run specific section: compare, ore, practical, density, dirac, or 'all'",
602 false, "all", "section", cmd
603 );
604
605 cmd.parse(argc, argv);
606
607 string section = sectionArg.getValue();
608
609 cout << "\n";
610 cout << "============================================================\n";
611 cout << " ALEPH-W HAMILTONIAN GRAPHS EXAMPLE\n";
612 cout << "============================================================\n";
613
614 if (section == "all" or section == "compare")
616
617 if (section == "all" or section == "ore")
619
620 if (section == "all" or section == "practical")
622
623 if (section == "all" or section == "density")
624 demo_density();
625
626 if (section == "all" or section == "dirac")
627 demo_dirac();
628
629 cout << "\n" << string(60, '=') << "\n";
630 cout << "Hamiltonian graphs demo completed!\n";
631 cout << string(60, '=') << "\n\n";
632
633 return 0;
634 }
635 catch (TCLAP::ArgException& e)
636 {
637 cerr << "Error: " << e.error() << " for argument " << e.argId() << endl;
638 return 1;
639 }
640 catch (exception& e)
641 {
642 cerr << "Error: " << e.what() << endl;
643 return 1;
644 }
645}
646
int main()
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 Dirac's sufficient condition for Hamiltonian graphs.
Tests Ore's sufficient condition for Hamiltonian graphs.
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2780
static void bar(int val, int scale=1)
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Hamiltonian sufficiency testing for graphs.
void print_subsection(const string &title)
void demo_density()
void demo_dirac()
void print_section(const string &title)
void demo_practical()
void build_complete_graph(UGraph &g, size_t n)
void demo_ore_theorem()
void demo_comparison()
void print_degrees(UGraph &g)
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
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.