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";
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";
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;
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;