Since Perl Weekly Challenge 351‘s tasks are “Special Average” and “Arithmetic Progression”, I decided that this week’s musical theme should be Average Person by Paul McCartney.
Task 1: Special Average
You are given an array of integers.
Write a script to return the average excluding the minimum and maximum of the given array.
Example 1
Input: @ints = (8000, 5000, 6000, 2000, 3000, 7000)
Output: 5250
Min: 2000
Max: 8000
Avg: (3000+5000+6000+7000)/4 = 21000/4 = 5250
Example 2
Input: @ints = (100_000, 80_000, 110_000, 90_000)
Output: 95_000
Min: 80_000
Max: 110_000
Avg: (100_000 + 90_000)/2 = 190_000/2 = 95_000
Example 3
Input: @ints = (2500, 2500, 2500, 2500)
Output: 0
Min: 2500
Max: 2500
Avg: 0
Example 4
Input: @ints = (2000)
Output: 0
Min: 2000
Max: 2000
Avg: 0
Example 5
Input: @ints = (1000, 2000, 3000, 4000, 5000, 6000)
Output: 3500
Min: 1000
Max: 6000
Avg: (2000 + 3000 + 4000 + 5000)/4 = 14000/4 = 3500
Approach
This task is pretty straightforward: one pass though the list to find the min and max values, then a second pass through to remove all values that match those min and max values, and then we count and sum them to generate an average.
Raku
Of course, once I started writing, I just naturally fell into using min and max to find the minimum and maximum values, and those are probably implemented in the compiler with loops, so I guess I’m doing three passes through the list.
sub specialAverage(@ints) {
my $min = @ints.min; # ok, using built-in functions to
my $max = @ints.max; # find min/max rather than a loop
@ints = @ints.grep({ $_ != $min && $_ != $max });
return 0 if @ints.elems == 0;
return @ints.sum / @ints.elems;
}View the entire Raku script for this task on GitHub.
$ raku/ch-1.raku
Example 1:
Input: @ints = (8000, 5000, 6000, 2000, 3000, 7000)
Output: 5250
Example 2:
Input: @ints = (100000, 80000, 110000, 90000)
Output: 95000
Example 3:
Input: @ints = (2500, 2500, 2500, 2500)
Output: 0
Example 4:
Input: @ints = (2000)
Output: 0
Example 5:
Input: @ints = (1000, 2000, 3000, 4000, 5000, 6000)
Output: 3500Perl
And because Perl has a plethora of CPAN modules descended from List::Util that provide min, max, and sum, I just used those from my favorite, List::AllUtils.
use List::AllUtils qw( min max sum );
sub specialAverage(@ints) {
my $min = min @ints; # ok, using List::Util functions to
my $max = max @ints; # find min/max rather than a loop
@ints = grep { $_ != $min && $_ != $max } @ints;
return 0 if @ints == 0;
return sum(@ints) / scalar(@ints);
}View the entire Perl script for this task on GitHub.
Python
Python, just like Raku, has the min, max, and sum functions built in.
def special_average(ints):
minv = min(ints) # ok, using built-in functions to
maxv = max(ints) # find min/max rather than a loop
ints = [ i for i in ints if i != minv and i != maxv ]
if len(ints) == 0: return 0
return int(sum(ints) / len(ints))View the entire Python script for this task on GitHub.
Elixir
And because the algorithm I’m implementing doesn’t have any early bailing out of these loops, I’m able to write it all up in a single, non-recursive Elixir function:
def special_average(ints) do
min = Enum.min(ints) # ok, using built-in functions to
max = Enum.max(ints) # find min/max rather than a loop
ints = Enum.filter(ints, fn i -> i != min and i != max end)
if length(ints) == 0 do
0
else
trunc(Enum.sum(ints) / length(ints))
end
endView the entire Elixir script for this task on GitHub.
Task 2: Arithmetic Progression
You are given an array of numbers.
Write a script to return true if the given array can be re-arranged to form an arithmetic progression, otherwise return false.
A sequence of numbers is called an arithmetic progression if the difference between any two consecutive elements is the same.
Example 1
Input: @num = (1, 3, 5, 7, 9)
Output: true
Already AP with common difference 2.
Example 2
Input: @num = (9, 1, 7, 5, 3)
Output: true
The given array re-arranged like (1, 3, 5, 7, 9) with common difference 2.
Example 3
Input: @num = (1, 2, 4, 8, 16)
Output: false
This is geometric progression and not arithmetic progression.
Example 4
Input: @num = (5, -1, 3, 1, -3)
Output: true
The given array re-arranged like (-3, -1, 1, 3, 5) with common difference 2.
Example 5
Input: @num = (1.5, 3, 0, 4.5, 6)
Output: true
The given array re-arranged like (0, 1.5, 3, 4.5, 6) with common difference 1.5.
Approach
We’re going to sort the elements from lowest to highest, then calculate the difference between the first two elements and make sure the differences between each subsequent pair matches.
Raku
At first, I pulled off the first two elements to calculate the diff, but then I realized I could save a line by just leaving the first value of $cur in the @ints list and just re-calculate the difference again the first time through the loop.
sub arithmeticProgression(@ints) {
@ints = @ints.sort;
my $prev = @ints.shift;
my $diff = @ints[0] - $prev; # calc first difference
for @ints -> $cur {
return False if $diff != $cur - $prev;
$prev = $cur;
}
return True;
}View the entire Raku script for this task on GitHub.
$ raku/ch-2.raku
Example 1:
Input: @ints = (1, 3, 5, 7, 9)
Output: True
Example 2:
Input: @ints = (9, 1, 7, 5, 3)
Output: True
Example 3:
Input: @ints = (1, 2, 4, 8, 16)
Output: False
Example 4:
Input: @ints = (5, -1, 3, 1, -3)
Output: True
Example 5:
Input: @ints = (1.5, 3, 0, 4.5, 6)
Output: TruePerl
The big difference between Raku and Perl is that we need to explicitly say we’re sorting @ints numerically.
sub arithmeticProgression(@ints) {
@ints = sort { $a <=> $b } @ints;
my $prev = shift @ints;
my $diff = $ints[0] - $prev; # calc first difference
foreach my $cur (@ints) {
return 'False' if $diff != $cur - $prev;
$prev = $cur;
}
return 'True';
}View the entire Perl script for this task on GitHub.
Python
There’s not much to say about the Python version.
def arithmetic_progression(ints):
ints = sorted(ints)
prev = ints.pop(0)
diff = ints[0] - prev # calc first difference
for cur in ints:
if diff != cur - prev: return False
prev = cur
return TrueView the entire Python script for this task on GitHub.
Elixir
However, because this algorithm does involve bailing out of the loop early if we fail a condition, I’ve implemented it as a recursive function. The assignment of prev = cur winds up happening on line 9 when we recursively call arithmetic_progression/3 with cur instead of prev.
def arithmetic_progression([], _, _), do: true
def arithmetic_progression([cur | ints], prev, diff) do
cond do
diff != cur - prev -> false
true -> arithmetic_progression(ints, cur, diff)
end
end
def arithmetic_progression(ints) do
ints = Enum.sort(ints)
{prev, ints} = {hd(ints), tl(ints)}
diff = hd(ints) - prev # calc first difference
arithmetic_progression(ints, prev, diff)
endView the entire Elixir script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-351/packy-anderson