Perl Weekly Challenge 157.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 157.
Task 1: Pythagorean Means
Submitted by: Mohammad S Anwar
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
I just apply the definitions. The arithmetic mean of a sequence of N numbers is their sum divided by N, their geometric mean is the N-th root of their product, and their harmonic mean is the inverse of the arithmetic mean of their inverses. That yields a simple oneliner. .
perl -MList::Util=sum0,product -E 'say "In: ", join ", ", @ARGV; say sprintf
"AM=%.2f GM=%.2f HM=%.2f", sum0(@ARGV)/@ARGV, product(@ARGV)**(1/@ARGV),
@ARGV/sum0(map {1/$_} (@ARGV));
' 1 3 5 6 9
perl -MList::Util=sum0,product -E 'say "In: ", join ", ", @ARGV;
say sprintf "AM=%.2f GM=%.2f HM=%.2f", sum0(@ARGV)/@ARGV, product(@ARGV)**(1/@ARGV),
@ARGV/sum0(map {1/$_} (@ARGV));
' 2 4 6 8 10
perl -MList::Util=sum0,product -E 'say "In: ", join ", ", @ARGV;
say sprintf "AM=%.2f GM=%.2f HM=%.2f", sum0(@ARGV)/@ARGV, product(@ARGV)**(1/@ARGV),
@ARGV/sum0(map {1/$_} (@ARGV));
' 1 2 3 4 5
Results:
In: 1, 3, 5, 6, 9
AM=4.80 GM=3.82 HM=2.76
In: 2, 4, 6, 8, 10
AM=6.00 GM=5.21 HM=4.38
In: 1, 2, 3, 4, 5
AM=3.00 GM=2.61 HM=2.19
My first geometric mean disagrees in the first example with that in the problem statement; I guess it is a typo.
The full version is almost identical. I add a few checks.
1 # Perl weekly challenge 157
2 # Task 1: Pythagorean means
3 #
4 # See https://wlmb.github.io/2022/03/21/PWC157/#task-1-pythagorean-means
5 use v5.12;
6 use warnings;
7 use List::Util qw(sum0 product any);
8 die "Usage: ./ch-1.pl N1 N2 N3 ... to get the means of the numbers Ni..." unless @ARGV;
9 say "In: ", join ", ", @ARGV;
10 my $AM= sprintf "%.2f", sum0(@ARGV)/@ARGV; # arithmetic mean
11 my $product=product(@ARGV);
12 my $GM=($product<0 && @ARGV%2==0)? "Undefined":sprintf "%.2f", $product**(1/@ARGV);
13 my $HM=(any{$_==0} (@ARGV))?"Undefined":sprintf "%.2f", @ARGV/sum0(map {1/$_} (@ARGV));
14 say sprintf "AM=$AM GM=$GM HM=$HM";
Examples:
./ch-1.pl 1 3 5 6 9
./ch-1.pl 2 4 6 8 10
./ch-1.pl 1 2 3 4 5
Results:
In: 1, 3, 5, 6, 9
AM=4.80 GM=3.82 HM=2.76
In: 2, 4, 6, 8, 10
AM=6.00 GM=5.21 HM=4.38
In: 1, 2, 3, 4, 5
AM=3.00 GM=2.61 HM=2.19
Examples with errors:
./ch-1.pl 0 1 2 3
./ch-1.pl 1 2 3 -4
Results:
In: 0, 1, 2, 3
AM=1.50 GM=0.00 HM=Undefined
In: 1, 2, 3, -4
AM=0.50 GM=Undefined HM=2.53
Task 2: Brazilian Number
Submitted by: Mohammad S Anwar
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 30 and
6 in base 4 is 12.
Example 3:
Input: $n = 8
Output: 1
Since 8 in base 3 is 22.
A Brazilian number in base B with M repeated digits D is of the form N=DDDD..DDD=D BM-1 + D BM-2+…+D B1+D B0=D(BM-1)/(B-1) after factorizing D and simplifying the geometric sum. As the largest value of D is B-1, N is smaller than BM. Thus, for a given base, B, I can find M as the smallest integer larger than log(N)/log(B). From M, I can obtain D=N*(B-1)/(D**{M}-1). Therefore, a number N is Brazilian if for some base between 2 and N-2, the number D obtained above is an integer. This yields the simple one liner:
perl -MPOSIX=ceil,floor -E 'N:for $n(@ARGV){for $b(2..$n-2){$m=ceil(log($n)/log($b));
$d=floor($n*($b-1)/($b**$m-1)); say("$n=", (($d)x$m)," in base $b is Brazilian"), next N
if $d*($b**$m-1)/($b-1)==$n} say "$n is not Brazilian"} ' `seq 30`
Results:
1 is not Brazilian
2 is not Brazilian
3 is not Brazilian
4 is not Brazilian
5 is not Brazilian
6 is not Brazilian
7=111 in base 2 is Brazilian
8=22 in base 3 is Brazilian
9 is not Brazilian
10=22 in base 4 is Brazilian
11 is not Brazilian
12=22 in base 5 is Brazilian
13=111 in base 3 is Brazilian
14=22 in base 6 is Brazilian
15=1111 in base 2 is Brazilian
16=22 in base 7 is Brazilian
17 is not Brazilian
18=33 in base 5 is Brazilian
19 is not Brazilian
20=22 in base 9 is Brazilian
21=111 in base 4 is Brazilian
22=22 in base 10 is Brazilian
23 is not Brazilian
24=44 in base 5 is Brazilian
25 is not Brazilian
26=222 in base 3 is Brazilian
27=33 in base 8 is Brazilian
28=44 in base 6 is Brazilian
29 is not Brazilian
30=33 in base 9 is Brazilian
The full code is:
1 # Perl weekly challenge 157
2 # Task 2: Brazilian number
3 #
4 # See https://wlmb.github.io/2022/03/21/PWC157/#task-2-brazilian-number
5 use v5.12;
6 use warnings;
7 use POSIX qw(ceil floor);
8 N:
9 for my $n(@ARGV){
10 for my $b(2..$n-2){
11 my $m=ceil(log($n)/log($b));
12 my $d=floor($n*($b-1)/($b**$m-1));
13 say("$n=", (($d)x$m)," in base $b is Brazilian"), next N
14 if $d*($b**$m-1)/($b-1)==$n
15 }
16 say "$n is not Brazilian"
17 }
./ch-2.pl `seq 30`
Results:
1 is not Brazilian
2 is not Brazilian
3 is not Brazilian
4 is not Brazilian
5 is not Brazilian
6 is not Brazilian
7=111 in base 2 is Brazilian
8=22 in base 3 is Brazilian
9 is not Brazilian
10=22 in base 4 is Brazilian
11 is not Brazilian
12=22 in base 5 is Brazilian
13=111 in base 3 is Brazilian
14=22 in base 6 is Brazilian
15=1111 in base 2 is Brazilian
16=22 in base 7 is Brazilian
17 is not Brazilian
18=33 in base 5 is Brazilian
19 is not Brazilian
20=22 in base 9 is Brazilian
21=111 in base 4 is Brazilian
22=22 in base 10 is Brazilian
23 is not Brazilian
24=44 in base 5 is Brazilian
25 is not Brazilian
26=222 in base 3 is Brazilian
27=33 in base 8 is Brazilian
28=44 in base 6 is Brazilian
29 is not Brazilian
30=33 in base 9 is Brazilian