## Perl Weekly Challenge 157: Pythagorean Means and Brazilian Number

These are some answers to the Week 157 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 March 27, 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: Pythagorean Numbers

*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.9, 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
```

Since the task description doesn’t explain it, let me provide some formulas for arithmetic mean (AM), geometric mean (GM) and harmonic mean (AM):

### Pythagorean Numbers in Raku

The Raku build-in `[ ]`

reduce metaoperator makes it possible to compute each of the means with just one simple code-line:

```
for (1,3,5,6,9), (2,4,6,8,10), (1,2,3,4,5) -> @in {
my $n = @in.elems;
my $am = ([+] @in)/ $n;
my $gm = ([*] @in)** (1/$n);
my $hm = $n / ([+] map { 1/$_}, @in);
printf "%-10s -> AM: %0.1f, GM: %0.1f, HM: %0.1f\n", "@in[]", $am, $gm, $hm;
}
```

This script displays the following output:

```
$ raku ./means.raku
1 3 5 6 9 -> AM: 4.8, GM: 3.8, HM: 2.8
2 4 6 8 10 -> AM: 6.0, GM: 5.2, HM: 4.4
1 2 3 4 5 -> AM: 3.0, GM: 2.6, HM: 2.2
```

### Pythagorean Numbers in Perl

Perl doesn’t have a built-in `reduce`

operator. We could either implement a `reduce`

subroutine (I’ve done that in some previous PWC challenge), or compute each mean separately. Here, I’ve chosen the second option.

```
use strict;
use warnings;
use feature "say";
for my $test ([1,3,5,6,9], [2,4,6,8,10], [1,2,3,4,5]) {
my @in = @$test;
my $n = scalar @in;
my $sum = 0;
$sum += $_ for @in;
my $am = $sum / $n;
my $prod = 1;
$prod *= $_ for @in;
my $gm = $prod ** (1/$n);
my $invsum = 0;
$invsum += 1/$_ for @in;
my $hm = $n / $invsum;
printf "%-10s -> AM: %0.1f, GM: %0.1f, HM: %0.1f\n", "@in", $am, $gm, $hm;
}
```

This script displays the following output:

$ perl ./means.pl 1 3 5 6 9 -> AM: 4.8, GM: 3.8, HM: 2.8 2 4 6 8 10 -> AM: 6.0, GM: 5.2, HM: 4.4 1 2 3 4 5 -> AM: 3.0, GM: 2.6, HM: 2.2

## Task 2: Brazilian numbers

*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.
```

Well to start with, I find the above definition of a Brazilian number to be not completely clear. This is another definition:

Brazilian” numbers are numbers n such that there is a natural number b with 1 < b < n-1 such that the representation of n in base b has all equal digits.

First, the condition `b < n-1`

is important because every number `n`

has representation 11 in base `n-1`

. Then, every even number `2P`

>= 8 is Brazilian, because `2P = 2(P-1) + 2`

, which is 22 in base `P-1`

when `P-1 > 2`

. Finally, we will extend the task to a search of all Brazilian numbers less than or equal to 36, since working with bases larger than 36 (the `1..9, 'a'..'z'`

range) would require a different data model.

### Brazilian Numbers in Raku

We use the Raku built-in base to convert the input number to a string using `$base`

as base.

```
sub is-brazilian (Int $n) {
return True if $n %% 2 and $n >= 8;
return False if $n <= 3;
for 2..^($n-1) -> $base {
return True if $n.base($base) ~~ /^(\d)$0+$/;
}
False;
}
say "Brazilian numbers less than or equal to 36 are:";
for 1..36 -> $m {
print "$m " if is-brazilian $m;
}
say "";
```

This script displays the following Brazilian numbers:

```
$ raku ./brazilian_number.raku
Brazilian numbers less than or equal to 36 are:
7 8 10 12 13 14 15 16 18 20 21 22 24 26 27 28 30 31 32 33 34 35 36
```

### Brazilian Numbers in Perl

The program below if basically a port to Perl of the Raku program above. The only significant change is that we had to implement the `to_base_b`

subroutine to perform decimal-to-some_base conversion.

```
use strict;
use warnings;
use feature qw /say/;
use constant DIGITS => ('0'..'9', 'A'..'Z');
sub to_base_b { # Converts decimal number to base b string
my($dec, $base) = @_;
my @digits;
while ($dec) {
unshift @digits, (DIGITS)[$dec % $base];
$dec = int($dec/$base);
}
return join "", @digits;
}
sub is_brazilian {
my $n = shift;
return 1 if $n % 2 == 0 and $n >= 8;
return 0 if $n <= 3;
for my $base (2..$n-2) {
return 1 if to_base_b($n, $base) =~ /^(\d)\1+$/;
}
0;
}
say "Brazilian numbers less than or equal to 36 are:";
for my $m (1..36) {
print "$m " if is_brazilian($m);
}
say "";
```

This program displays the following output:

```
$ perl brazilian_number.pl
Brazilian numbers less than or equal to 36 are:
7 8 10 12 13 14 15 16 18 20 21 22 24 26 27 28 30 31 32 33 34 35 36
```

## 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 3, 2022. And, please, also spread the word about the Perl Weekly Challenge if you can.

## Leave a comment