Perl Weekly Challenge 160.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 160.

Task 1: Four Is Magic

Submitted by: Mohammad S Anwar
You are given a positive number, $n < 10.

Write a script to generate english text sequence starting with
the English cardinal representation of the given number, the
word ‘is’ and then the English cardinal representation of the
count of characters that made up the first word, followed by a
comma. Continue until you reach four.


Example 1:
Input: $n = 5
Output: Five is four, four is magic.

Example 2:
Input: $n = 7
Output: Seven is five, five is four, four is magic.

Example 3:
Input: $n = 6
Output: Six is three, three is five, five is four, four is
magic.

This is simple, as four is indeed magic (see below). We initialize an array with the digit names and use their length as an index to the next name, starting from the supplied argument. This fits into a oneliner.

perl -E '@e=qw(zero one two three four five six seven eight nine); for $n(@ARGV){
while($n!=4){print "$e[$n] is ", $e[$n=length $e[$n]], ", "} say "four is magic"};
' 0 1 2 3 4 5 6 7 8 9

Results:

zero is four, four is magic
one is three, three is five, five is four, four is magic
two is three, three is five, five is four, four is magic
three is five, five is four, four is magic
four is magic
five is four, four is magic
six is three, three is five, five is four, four is magic
seven is five, five is four, four is magic
eight is five, five is four, four is magic
nine is four, four is magic

The corresponding full program would be:

 1  # Perl weekly challenge 160
 2  # Task 1: Four is magic
 3  #
 4  # See https://wlmb.github.io/2022/04/11/PWC160/#task-1-four-is-magic
 5  use v5.12;
 6  use warnings;
 7  die "Usage: ./ch-1.pl N1 [N2... ]\n".
 8      "to find all trajectories between number names starting at index N1, N2..."
 9       unless @ARGV;
10  my @names=qw(zero one two three four five six seven eight nine);
11  for my $n(@ARGV){
12      say("Wrong input: $n"), next if $n>=@names;
13      while($n!=4){
14          print "$names[$n] is ", $names[$n=length $names[$n]], ", ";
15      }
16      say "four is magic"
17  };

Example:

./ch-1.pl 0 1 2 3 4 5 6 7 8 9 10

Results:

zero is four, four is magic
one is three, three is five, five is four, four is magic
two is three, three is five, five is four, four is magic
three is five, five is four, four is magic
four is magic
five is four, four is magic
six is three, three is five, five is four, four is magic
seven is five, five is four, four is magic
eight is five, five is four, four is magic
nine is four, four is magic
Wrong input: 10

The reason four is magic is that it has four letters, and that the sequence started from any number converges to four. Thus, instead of hard-coding the magic four, we may test for a fixed point of the iteration that maps a $name to the $name[length $name]. This allows the problem to be generalized to an arbitrary array and an arbitrary mapping of the array onto itself. The problem then is that there could be cycles as well as fixed points, which would lead to infinite loops if they are not detected. Thus, the following more general and more robust program. I still use the length of the string as a mapping function, but it could be changed.

 1  # Perl weekly challenge 160
 2  # Task 1: Four is magic
 3  #
 4  # See https://wlmb.github.io/2022/04/11/PWC160/#task-1-four-is-magic
 5  use v5.12;
 6  use warnings;
 7  die <<'END' unless @ARGV >= 2;
 8  Usage: ./ch-1a.pl "S1 [S2... ]" N1 [N2...]\n
 9      to find all trajectories between strings S1 S2...
10      starting from position N1, N2...
11      The mapping function uses the length of a string as an index
12      to the next string
13  END
14  my @strings=split " ", shift;
15  foreach my $index(@ARGV){
16      my @seen;
17      while(1){
18          say("$strings[$index] is magic"), last if (my $next=next_index($index))==$index;
19          say("$strings[$index] is magic loop"), last if $seen[$index];
20          say("$index->nothing"), last unless defined $next;
21          $seen[$index]++;
22          print "$strings[$index] is $strings[$next], ";
23          $index=$next;
24      }
25  }
26  sub next_index {
27      my $current=shift;
28      my $next=length $strings[$current]; # Could use other mappings
29      return undef unless defined $strings[$next];
30      return $next;
31  }

Example:

./ch-1a.pl "zero one two three four five six seven eight nine" 0 1 2 3 4 5 6 7 8 9

Results:

zero is four, four is magic
one is three, three is five, five is four, four is magic
two is three, three is five, five is four, four is magic
three is five, five is four, four is magic
four is magic
five is four, four is magic
six is three, three is five, five is four, four is magic
seven is five, five is four, four is magic
eight is five, five is four, four is magic
nine is four, four is magic

Another example, with different strings,

./ch-1a.pl "red orange yellow green blue indigo violet" 0 1 2 3 4 5 6 7

Results:

red is green, green is indigo, indigo is violet, violet is magic
orange is violet, violet is magic
yellow is violet, violet is magic
green is indigo, indigo is violet, violet is magic
blue is magic
indigo is violet, violet is magic
violet is magic
7->nothing

In this case, there are two magic color names, as blue occupies position 4 (starting from 0) and it has four letters, while violet occupies position 6 and has six letters.

Task 2: Equilibrium Index

Submitted by: Mohammad S Anwar
You are give an array of integers, @n.

Write a script to find out the Equilibrium Index of the given
array, if found.

For an array A consisting n elements, index i is an
equilibrium index if the sum of elements of subarray A[0…i-1]
is equal to the sum of elements of subarray A[i+1…n-1].


Example 1:
Input: @n = (1, 3, 5, 7, 9)
Output: 3

Example 2:
Input: @n = (1, 2, 3, 4, 5)
Output: -1 as no Equilibrium Index found.

Example 3:
Input: @n = (2, 4, 2)
Output: 1

To solve this task I can sum the leading and trailing subarrays for all possible cuts. I use PDL’s ability to slice arrays and operate on its elements in the following one-liners.

perl -MPDL -MPDL::NiceSlice -E 'say "Input: ", $x=pdl(@ARGV); $i="-1 No eq. found";
    for(1..$x->nelem-2){$i=$_ if $x(0:$_-1)->sumover==$x($_+1:-1)->sumover} say "Output: $i"
' 1 3 5 7 9
perl -MPDL -MPDL::NiceSlice -E 'say "Input: ", $x=pdl(@ARGV); $i="-1 No eq. found";
    for(1..$x->nelem-2){$i=$_ if $x(0:$_-1)->sumover==$x($_+1:-1)->sumover} say "Output: $i"
' 1 2 3 4 5
perl -MPDL -MPDL::NiceSlice -E 'say "Input: ", $x=pdl(@ARGV); $i="-1 No eq. found";
    for(1..$x->nelem-2){$i=$_ if $x(0:$_-1)->sumover==$x($_+1:-1)->sumover} say "Output: $i"
' 2 4 2

Results:

Input: [1 3 5 7 9]
Output: 3
Input: [1 2 3 4 5]
Output: -1 No eq. found
Input: [2 4 2]
Output: 1

The full version follows.

 1  # Perl weekly challenge 160
 2  # Task 2: Equilibrium index
 3  #
 4  # See https://wlmb.github.io/2022/04/11/PWC160/#task-2-equilibrium-index
 5  use v5.12;
 6  use warnings;
 7  use PDL;
 8  use PDL::NiceSlice;
 9  die "Usage: ./ch-2.pl N1 [N2...] to find equilibrium index of an array of numbers"
10      unless @ARGV;
11  my $input=pdl(@ARGV);
12  my $result="-1 as no equilibrium index was found"; # default output
13  say "Input: ", $input;
14  for(1..$input->nelem-2){ #for all internal indices
15     $result="$_ as sum(".$input(0:$_-1).")==sum(".$input($_+1:-1).")", last
16     if $input(0:$_-1)->sumover==$input($_+1:-1)->sumover; # Found equilibrium
17  }
18  say "Output: $result";

Examples:

./ch-2.pl 1 3 5 7 9
./ch-2.pl 1 2 3 4 5
./ch-2.pl 2 4 2

Results:

Input: [1 3 5 7 9]
Output: 3 as sum([1 3 5])==sum([9])
Input: [1 2 3 4 5]
Output: -1 as no equilibrium index was found
Input: [2 4 2]
Output: 1 as sum([2])==sum([2])

Notice that the equilibrium point as defined above is not the center of mass of the array, as the array elements are summed disregarding their index with respect to the equilibrium index, accounting only for their sign (left or right). Thus the simpler calculation of the center of mass (the first moment of the array) wouldn’t work.

Written on April 11, 2022