perl logo Perl logo (Thanks to Olaf Alders)

The weekly challenge 382 - Task 1: Hamiltonian Cycle

 1 #!/usr/bin/env perl
 2 # https://theweeklychallenge.org/blog/perl-weekly-challenge-382/#TASK1
 3 #
 4 # Task 1: Hamiltonian Cycle
 5 # =========================
 6 #
 7 # You are given a target number.
 8 #
 9 # Write a script to arrange all the whole numbers from 1 up to the given target
10 # number into a circle so that every pair of side-by-side numbers adds up to a
11 # perfect square. Please make sure, the last number and the first must also add
12 # up to a square.
13 #
14 ## Example 1
15 ##
16 ## Input: $n = 32
17 ## Output: 1, 8, 28, 21, 4, 32, 17, 19, 30, 6, 3, 13, 12, 24, 25, 11, 5, 31, 18,
18 ## 7, 29, 20, 16, 9, 27, 22, 14, 2, 23, 26, 10, 15
19 ##
20 ## 1  + 8  = 9
21 ## 8  + 28 = 36
22 ## 28 + 21 = 49
23 ## 21 + 4  = 25
24 ## 4  + 32 = 36
25 ## 32 + 17 = 49
26 ## 17 + 19 = 36
27 ## 19 + 30 = 49
28 ##
29 ## so on, all the way through the sequence.
30 #
31 ## Example 2
32 ##
33 ## Input: $n = 15
34 ## Output: ()
35 ##
36 ## No valid circular list of numbers exists.
37 #
38 ## Example 3
39 ##
40 ## Input: $n = 34
41 ## Output: 1, 8, 28, 21, 4, 32, 17, 19, 6, 30, 34, 15, 10, 26, 23, 2, 14, 22,
42 ## 27, 9, 16, 33, 31, 18, 7, 29, 20, 5, 11, 25, 24, 12, 13, 3
43 #
44 ############################################################
45 ##
46 ## discussion
47 ##
48 ############################################################
49 #
50 # We recursively try to find a hamiltonian cycle:
51 # - We pick the "1" at the beginning as the current last element
52 # - from all remaining numbers in the 1..$n list, we pick each one
53 # - if adding it to the last element creates a square, we recursively check
54 #   if we can turn it into a hamiltonian cycle, with the new last element
55 # - if we have a hamiltonian cycle, we are done, otherwise backtrack and
56 #   check for other potential solutions until we run out of possibilities
57 
58 use v5.36;
59 
60 hamiltonian_cycle(32);
61 hamiltonian_cycle(15);
62 hamiltonian_cycle(34);
63 
64 sub hamiltonian_cycle($n) {
65     say "Input $n";
66     my @numbers = (1..$n);
67     say join(", ", @numbers);
68     my @seq = ();
69     push @seq, shift @numbers;
70     my @found = find_hamiltonian_cycle(0, $seq[0], @numbers);
71     push @seq, @found;
72     return say "Output: (" . join(", ", @seq) . ")" if @found;
73     say "Output: ()";
74 }
75 
76 sub find_hamiltonian_cycle($indent, $last, @rest) {
77     if(scalar(@rest) == 1) {
78         return @rest if is_square($last + $rest[0]) && is_square($rest[0] + 1);
79         return ();
80     }
81     foreach my $i (0..$#rest) {
82         next unless is_square($last + $rest[$i]);
83         my @found = find_hamiltonian_cycle($indent + 1, $rest[$i], @rest[0..$i-1, $i+1..$#rest]);
84         next unless @found;
85         next unless is_square($found[$#found] + 1);
86         return ($rest[$i], @found);
87     }
88     return ();
89 }
90 
91 sub is_square($n) {
92     my $root = sqrt($n);
93     return $root == int($root);
94 }
95