Perl Weekly Challenge 160: Four is Magic and Equilibrium Index

These are some answers to the Week 160 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a couple of days from now (on April 17, 2022 at 24:00). This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.

Task 1: Four is Magic

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.

Four is four letter-long, so we might enter into an endless loop if the specific case for four did not exist: “four is four, four is four, four is four, etc.” This is the only case where the English name for a digit has a number of letters equal to the digit. Note that we are also lucky that, with English names of integers, we don’t get into an endless loop involving two (or more) numbers. If 5 was written “fiv” in English, we would get into this endless loop between 5 and 3: “fiv is three, three is fiv, fiv is three, etc.” So we are rather lucky that this doesn’t happen in English, but there are certainly other languages where this wouldn’t work.

Four is Magic in Raku

That’s fairly straight forward. We define an array @numbers of integers between 0 and 9 spelled in English. We then iterate through the numbers until we reach 4, at which point we break out of the loop with a return statement.

sub is-magic (Int $n is copy) {
    my @numbers = <zero one two three four five six seven eight nine>;
    my $output = "";
    loop {
        my $letter-count = @numbers[$n].chars;
        if $n == 4 {
            return $output ~ "four is magic.";
        } else {
            $output ~= "@numbers[$n] is @numbers[$letter-count], ";
            $n = $letter-count;
        }
    }
}
for 1..9 -> $n {
  say "$n: ", is-magic($n).tc;
}

This program displays the following output:

$ raku ./magic4.raku
1: One is three, three is five, five is four, four is magic.
2: Two is three, three is five, five is four, four is magic.
3: Three is five, five is four, four is magic.
4: Four is magic.
5: Five is four, four is magic.
6: Six is three, three is five, five is four, four is magic.
7: Seven is five, five is four, four is magic.
8: Eight is five, five is four, four is magic.
9: Nine is four, four is magic.

Four is Magic in Perl

This is essentially the same in Perl. We define an array @numbers of integers between 0 and 9 spelled in English. We then iterate through the numbers until we reach 4, at which point we break out of the loop with a return statement.

use strict;
use warnings;
use feature "say";

sub is_magic {
    my $n = shift;
    my @numbers = qw<zero one two three four five six seven eight nine>;
    my $output = "";
    while (1) {
        my $letter_count = length $numbers[$n];
        if ($n == 4) {
            return $output . "four is magic.";
        } else {
            $output .= "$numbers[$n] is $numbers[$letter_count], ";
            $n = $letter_count;
        }
    }
}
for my $m (1..9) {
  say "$m: ", ucfirst(is_magic($m));
}

This program displays the following output:

$ perl magic4.pl
1: One is three, three is five, five is four, four is magic.
2: Two is three, three is five, five is four, four is magic.
3: Three is five, five is four, four is magic.
4: Four is magic.
5: Five is four, four is magic.
6: Six is three, three is five, five is four, four is magic.
7: Seven is five, five is four, four is magic.
8: Eight is five, five is four, four is magic.
9: Nine is four, four is magic.

Task 2: Equilibrium Index

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

With the small arrays of the examples, we will use a brute force approach: simply testing all possible indices within the range. If the arrays were significantly larger, we might try to predict faster the proper index. The best solution that I can think of may be a binary search approach over the possible indices within the range. For example, with the (1, 2, 3, 4, 5) array, we test the middle item (3), and find that 1 + 2 < 4 + 5. So we try the (1, 2, 3) sub-array and test the middle item (2), and find that 1 < 3. At this point, we can figure that there will be no solution.

Equilibrium Index in Raku

The brute force approach described above is pretty straight forward: we test all possible indices and return the proper index if the sum of the items before it equals the sum of the items after it. If we reach the end of the loop, then there is no solution and we return -1.

sub equilibrium (@ary) {
    for 1..@ary.end-1 -> $i {
        return $i if @ary[0..$i-1].sum == @ary[$i+1..@ary.end].sum;
    }
    return -1;
}
for <1 3 5 7 9>, <1 2 3 4 5>, <2 4 2> -> @a {
    say "@a[]".fmt("%-12s"), " -> ", equilibrium(@a);
}

This script displays the following output:

$ raku ./equilibrium.raku
1 3 5 7 9    -> 3
1 2 3 4 5    -> -1
2 4 2        -> 1

Equilibrium Index in Perl

Again the same brute force approach: we test all possible indices and return the proper index if the sum of the items before it equals the sum of the items after it. If we reach the end of the loop, then there is no solution and we return -1. Note that, in Perl, we had to implement our own sum subroutine since it does not exist as a built-in function. Obviously, I could have used the sum subroutine of various core modules, but, as I have stated many times, I echew using off-the-shelf modules or packages in a coding challenge.

use strict;
use warnings;
use feature "say";

sub sum {
    my $sum = 0;
    $sum += $_ for @_;
    return $sum;
}
sub equilibrium {
    my @ary = @_;
    for my $i (1..$#ary-1) {
        return $i if sum (@ary[0..$i-1]) == sum (@ary[$i+1..$#ary]);
    }
    return -1;
}
for my $a ([1, 3, 5, 7, 9], [1, 2, 3, 4, 5], [2, 4, 2]) {
    my $formated = sprintf "%-12s", "@$a";
    say "$formated -> ", equilibrium(@$a);
}

This program displays the following output:

$ perl ./equilibrium.pl
1 3 5 7 9    -> 3
1 2 3 4 5    -> -1
2 4 2        -> 1

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on April 24, 2022. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.