Perl Weekly Challenge 122.
My solutions (task 1 and task 2) to the The Weekly Challenge - 122.
Task 1: Average of Stream
Submitted by: Mohammad S Anwar
You are given a stream of numbers, @N.
Write a script to print the average of the stream at every point.
Example
Input: @N = (10, 20, 30, 40, 50, 60, 70, 80, 90, ...)
Output: 10, 15, 20, 25, 30, 35, 40, 45, 50, ...
Average of first number is 10.
Average of first 2 numbers (10+20)/2 = 15
Average of first 3 numbers (10+20+30)/3 = 20
Average of first 4 numbers (10+20+30+40)/4 = 25 and so on.
By keeping the current total and the number of data items the problem becomes a one-liner. I take input from STDIN, assuming that in a real application the data might not be known before the program is launched, so I can pipe in the data.
perl -E '$i=1; say 10 * $i++ while $i<10;' | # data source \
perl -E 'while(<>){chomp; say "Input: ", $_, " Output:", ($T+=$_)/(++$N);}' # running average
Results:
Input: 10 Output:10
Input: 20 Output:15
Input: 30 Output:20
Input: 40 Output:25
Input: 50 Output:30
Input: 60 Output:35
Input: 70 Output:40
Input: 80 Output:45
Input: 90 Output:50
The full program would be:
# Perl weekly challenge 122
# Task 1: Average of stream
#
# See https://wlmb.github.io/2021/07/19/PWC122/#task-1-average-of-stream
use strict;
use warnings;
use v5.12;
my $counter=0; # item counter
my $total=0; # running total
while(<>){
chomp;
++$counter;
$total += $_;
my $average=$total/$counter;
say "Input: $_ Output: $average";
}
Example:
perl -E '$i=1; say 10 * $i++ while $i<10;' | ./ch-1.pl
Results:
Input: 10 Output: 10
Input: 20 Output: 15
Input: 30 Output: 20
Input: 40 Output: 25
Input: 50 Output: 30
Input: 60 Output: 35
Input: 70 Output: 40
Input: 80 Output: 45
Input: 90 Output: 50
Other examples
-
Average of triangular numbers
perl -E '$i=1; say $i++ while $i<10;' |./ch-1.pl
Results:
Input: 1 Output: 1 Input: 2 Output: 1.5 Input: 3 Output: 2 Input: 4 Output: 2.5 Input: 5 Output: 3 Input: 6 Output: 3.5 Input: 7 Output: 4 Input: 8 Output: 4.5 Input: 9 Output: 5
-
Average of first squares:
perl -E '$i=1; say +($i++)**2 while $i<10;' |./ch-1.pl
Results:
Input: 1 Output: 1 Input: 4 Output: 2.5 Input: 9 Output: 4.66666666666667 Input: 16 Output: 7.5 Input: 25 Output: 11 Input: 36 Output: 15.1666666666667 Input: 49 Output: 20 Input: 64 Output: 25.5 Input: 81 Output: 31.6666666666667
-
Average of first cubes:
perl -E '$i=1; say +($i++)**3 while $i<10;' |./ch-1.pl
Results:
Input: 1 Output: 1 Input: 8 Output: 4.5 Input: 27 Output: 12 Input: 64 Output: 25 Input: 125 Output: 45 Input: 216 Output: 73.5 Input: 343 Output: 112 Input: 512 Output: 162 Input: 729 Output: 225
Task 2: Basketball Points
Submitted by: Mohammad S Anwar
You are given a score $S.
You can win basketball points e.g. 1 point, 2 points and 3
points.
Write a script to find out the different ways you can score
$S.
Example
Input: $S = 4
Output: 1 1 1 1
1 1 2
1 2 1
1 3
2 1 1
2 2
3 1
Input: $S = 5
Output: 1 1 1 1 1
1 1 1 2
1 1 2 1
1 1 3
1 2 1 1
1 2 2
1 3 1
2 1 1 1
2 1 2
2 2 1
2 3
3 1 1
3 2
I solve the problem recursively. The possible sets of points
for a given score S are those for a lower score S-1
, S-2
or S-3
followed by a single, double or triple point. I just
have to be careful with the initial conditions, either a
single, a double or a triple score.
# Perl weekly challenge 122
# Task 2:
# Basketball Points
# See https://wlmb.github.io/2021/07/19/PWC122/#task-2-basketball-points
use strict;
use warnings;
use v5.12;
use Memoize;
memoize("points");
foreach (@ARGV){
say "Input: $_\nOutput:\n\t", join "\n\t", map {join " ", @$_} points($_);
}
sub points{
my $s=shift;
return () if $s<=0;
# Append a 1, 2 or 3 point throw to the previous points
my @result=((map {my @x=@$_;push @x,1; [@x]}points($s-1)),
(map {my @x=@$_;push @x,2; [@x]}points($s-2)),
(map {my @x=@$_;push @x,3; [@x]}points($s-3)));
push @result, [$s] if $s<=3;
return @result;
}
Examples:
./ch-2.pl 4 5
Results:
Input: 4
Output:
1 1 1 1
2 1 1
1 2 1
3 1
1 1 2
2 2
1 3
Input: 5
Output:
1 1 1 1 1
2 1 1 1
1 2 1 1
3 1 1
1 1 2 1
2 2 1
1 3 1
1 1 1 2
2 1 2
1 2 2
3 2
1 1 3
2 3
Memoization makes a difference for large scores. For example,
for S=24
it took about 5 times longer to obtain the result
without memoization compared with the result with
memoization. However, memoization can produce surprises. For
example, a line such as
@result=((map {push @$_,1; $_}points($s-1),...);
(which I wrote in my first attempt)
would work without memoization but it fails with memoization,
as the variable $_
from previous results would be
inadvertently modified. Thus, the need for the temporal
variable @x
.
Larger example:
time (./ch-2.pl 25 | wc) 2>&1
Results:
2555759 40058424 82672617
real 0m15.589s
user 0m13.761s
sys 0m1.867s
which means the program found 2555759-2=2555757
different
sets of points for a total score of 25 in 15s in my laptop.