Perl Weekly Challenge: Week 157

Challenge 1:

Pythagorean Means

You are given a set of integers.

Write a script to compute all three Pythagorean Means i.e Arithmetic Mean, Geometric Mean and Harmonic Mean of the given set of integers. Please refer to wikipedia page for more informations.

Example 1:
Input: @n = (1,3,5,6,9)
Output: AM = 4.8, GM = 3.8, HM = 2.8
Example 2:
Input: @n = (2,4,6,8,10)
Output: AM = 6.0, GM = 5.2, HM = 4.4
Example 3:
Input: @n = (1,2,3,4,5)
Output: AM = 3.0, GM = 2.6, HM = 2.2

All I needed to do for this problem was to translate the formulas given in the Wikipedia article into Raku and Perl. So there isn't really much to say about the code.

my $am = ([+] @n) / @n.elems;

Well ok one thing; From the examples it would seem that we only should output one decimal digit of precision. .round() takes care of that.

my $gm = ((([*] @n).abs) ** (-1 / -@n.elems)).round(0.1);
my $hm = (@n.elems / ([+]  @n.map({ 1 / $_; }))).round(0.1);

say "AM = $am, GM = $gm, HM = $hm";

(Full code on Github.)

For Perl, I used nearest() from Math::Round to do the rounding. I also reused sum() and product() from previous challenges to deal with the lack of [+] and [*].

my $am = sum(\@n) / scalar @n;
my $gm = nearest(0.1, (abs product(\@n)) ** (-1 / scalar -@n));
my $hm = nearest(0.1, scalar @n / sum([map { 1 / $_; } @n]));

say "AM = $am, GM = $gm, HM = $hm";

(Full code on Github.)

Challenge 2:

Brazilian Number

You are given a number $n > 3.

Write a script to find out if the given number is a Brazilian Number.

A positive integer number N has at least one natural number B where 1 < B < N-1 where the representation of N in base B has same digits.

Example 1:
Input: $n = 7
Output: 1

Since 7 in base 2 is 111.
Example 2:
Input: $n = 6
Output: 0

Since 6 in base 2 is 110,
    6 in base 3 is 20 and
    6 in base 4 is 12.
Example 3:
Input: $n = 8
Output: 1

Since 8 in base 3 is 22.

This was another easy one for Raku:

for 2 ..^ $n - 1 -> $B {
    my @digits = $n.base($B).comb;

If all the digits are the same as the first one, we print 1 and finish the script.

    if @digits.all == @digits[0] {
        say 1;
        exit;
    }
}

If we've gone through the full range of bases, $n is not a Brazilian number so we print 0.

say 0;

(Full code on Github.)

Perl required a replacement for .base(). Luckily I had written such a thing way back in Challenge 43. That function returned a string so I had to make a slight modification to make it return a number. As it has been a long time, here it is:

sub base {
    my ($number, $base) = @_;
    my @digits = (0 .. 9, 'A' .. 'Z');
    my @result;
    while ($number > ($base - 1)) {
        my $digit = $number % $base;
        push @result, $digits[$digit];
        $number /=  $base;
    }
    push @result, $digits[$number];

    return 0 + (join '', reverse @result);
}

Back to the main part of the script...

my $n = shift // die "Need an integer.\n";

for my $B (2 .. $n - 2) {
    my @digits = split //, base($n, $B);

As a workaround for no .all(), I grepped for a digit that was not the same as the first digit.

    if (!grep { $_ != $digits[0] } @digits) {
        say 1;
        exit;
    }
}

say 0;

(Full code on Github.)