Perl Weekly Challenge 343 starts with “Zero Friend” (which made me think of Carole King), but with task two being “Champion Team”, I couldn’t pick any music besides We Are The Champions by Queen.
So now let’s hope I don’t make any bad mistakes while tackling PWC 343!
Task 1: Zero Friend
You are given a list of numbers.
Find the number that is closest to zero and return its distance to zero.
Example 1
Input: @nums = (4, 2, -1, 3, -2)
Output: 1
Values closest to 0: -1 and 2 (distance = 1 and 2)
Example 2
Input: @nums = (-5, 5, -3, 3, -1, 1)
Output: 1
Values closest to 0: -1 and 1 (distance = 1)
Example 3
Input: @ums = (7, -3, 0, 2, -8)
Output: 0
Values closest to 0: 0 (distance = 0)
Exact zero wins regardless of other close values.
Example 4
Input: @nums = (-2, -5, -1, -8)
Output: 1
Values closest to 0: -1 and -2 (distance = 1 and 2)
Example 5
Input: @nums = (-2, 2, -4, 4, -1, 1)
Output: 1
Values closest to 0: -1 and 1 (distance = 1)
Approach
This one is easy-peasy. The distance of $n
from 0
is abs($n)
, so all we have to do is find the minimum value for abs($n)
. I don’t understand why the explanations for examples 1 ad 4 talk about other values that aren’t as close, though.
Raku
In Raku, it’s a one-liner.
sub zeroFriend(@nums) {
@nums.map({ abs($_) }).min;
}
View the entire Raku script for this task on GitHub.
$ raku/ch-1.raku
Example 1:
Input: @nums = (4, 2, -1, 3, -2)
Output: 1
Example 2:
Input: @nums = (-5, 5, -3, 3, -1, 1)
Output: 1
Example 3:
Input: @nums = (7, -3, 0, 2, -8)
Output: 0
Example 4:
Input: @nums = (-2, -5, -1, -8)
Output: 1
Example 5:
Input: @nums = (-2, 2, -4, 4, -1, 1)
Output: 1
Perl
So’s Perl…
use List::AllUtils qw( min );
sub zeroFriend(@nums) {
min map { abs($_) } @nums;
}
View the entire Perl script for this task on GitHub.
Python
As is Python…
def zero_friend(nums):
return min([ abs(n) for n in nums])
View the entire Python script for this task on GitHub.
Elixir
And Elixir!
def zero_friend(nums) do
nums |> Enum.map(fn n -> abs(n) end) |> Enum.min
end
View the entire Elixir script for this task on GitHub.
Task 2: Champion Team
You have n teams in a tournament. A matrix grid tells you which team is stronger between any two teams:
If grid[i][j] == 1, then team i is stronger than team j
If grid[i][j] == 0, then team j is stronger than team i
Find the champion team – the one with most wins, or if there is no single such team, the strongest of the teams with most wins. (You may assume that there is a definite answer.)
Example 1
Input: @grid = (
[0, 1, 1],
[0, 0, 1],
[0, 0, 0],
)
Output: Team 0
[0, 1, 1] => Team 0 beats Team 1 and Team 2
[0, 0, 1] => Team 1 beats Team 2
[0, 0, 0] => Team 2 loses to all
Example 2
Input: @grid = (
[0, 1, 0, 0],
[0, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0],
)
Output: Team 3
[0, 1, 0, 0] => Team 0 beats only Team 1
[0, 0, 0, 0] => Team 1 loses to all
[1, 1, 0, 0] => Team 2 beats Team 0 and Team 1
[1, 1, 1, 0] => Team 3 beats everyone
Example 3
Input: @grid = (
[0, 1, 0, 1],
[0, 0, 1, 1],
[1, 0, 0, 0],
[0, 0, 1, 0],
)
Output: Team 0
[0, 1, 0, 1] => Team 0 beats teams 1 and 3
[0, 0, 1, 1] => Team 1 beats teams 2 and 3
[1, 0, 0, 0] => Team 2 beats team 0
[0, 0, 1, 0] => Team 3 beats team 2
Of the teams with 2 wins, Team 0 beats team 1.
Example 4
Input: @grid = (
[0, 1, 1],
[0, 0, 0],
[0, 1, 0],
)
Output: Team 0
[0, 1, 1] => Team 0 beats Team 1 and Team 2
[0, 0, 0] => Team 1 loses to Team 2
[0, 1, 0] => Team 2 beats Team 1 but loses to Team 0
Example 5
Input: @grid = (
[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 0, 1, 1],
[1, 1, 0, 0, 0],
[1, 1, 0, 1, 0],
)
Output: Team 2
[0, 0, 0, 0, 0] => Team 0 loses to all
[1, 0, 0, 0, 0] => Team 1 beats only Team 0
[1, 1, 0, 1, 1] => Team 2 beats everyone except self
[1, 1, 0, 0, 0] => Team 3 loses to Team 2
[1, 1, 0, 1, 0] => Team 4 loses to Team 2
Approach
Where the last problem depended on finding the minimal value, this problem depends on finding the maximal value. In most of the examples, there’s a clear winner by number of wins. But in example 4, both teams 0 and 1 have two wins, so we have to resort to the “stronger” check in the description, which is basically which team won in the head-to-head competition (in example 4, it’s team 0).
So the approach will be to make a pass over the matrix and sum each row to get a count of wins. If that produces a clear winner, we return. Otherwise if there’s a tie, we go back to the matrix.
Raku
Initially, I thought I wanted to make an array of the number of wins for each team, but then I realized I would have to find the max value, and then go back to figure out which team had that number of wins. It would be just easier to do one loop, keep a running maximum, and keep a list of teams that it that maximum.
sub stronger($i, $j, @grid) {
return $i if @grid[$i][$j] == 1;
return $j if @grid[$i][$j] == 0;
}
sub championTeam(@grid) {
my @best;
my $max = 0;
for @grid.keys -> $team {
my $sum = @grid[$team].sum;
if ($sum > $max) {
@best = ($team);
$max = $sum;
}
elsif ($sum == $max) {
@best.push($team);
}
}
if (@best.elems == 1) {
return @best[0];
}
my ($i, $j) = @best;
return stronger($i, $j, @grid);
}
View the entire Raku script for this task on GitHub.
$ raku/ch-2.raku
Example 1:
Input: @grid = [
[0, 1, 1],
[0, 0, 1],
[0, 0, 0]
]
Output: Team 0
Example 2:
Input: @grid = [
[0, 1, 0, 0],
[0, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0]
]
Output: Team 3
Example 3:
Input: @grid = [
[0, 1, 0, 1],
[0, 0, 1, 1],
[1, 0, 0, 0],
[0, 0, 1, 0]
]
Output: Team 0
Example 4:
Input: @grid = [
[0, 1, 1],
[0, 0, 0],
[0, 1, 0]
]
Output: Team 0
Example 5:
Input: @grid = [
[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 0, 1, 1],
[1, 1, 0, 0, 0],
[1, 1, 0, 1, 0]
]
Output: Team 2
Perl
The Perl solution is the same as the Raku one, with just a few syntax differences.
use List::AllUtils qw( sum );
sub stronger($i, $j, @grid) {
return $i if $grid[$i][$j] == 1;
return $j if $grid[$i][$j] == 0;
}
sub championTeam(@grid) {
my @best;
my $max = 0;
foreach my $team ( 0 .. $#grid ) {
my $sum = sum(@{$grid[$team]});
if ($sum > $max) {
@best = ($team);
$max = $sum;
}
elsif ($sum == $max) {
push @best, $team;
}
}
if (@best == 1) {
return $best[0];
}
my ($i, $j) = @best;
return stronger($i, $j, @grid);
}
View the entire Perl script for this task on GitHub.
Python
Even the Python version manages to look the same.
def stronger(i, j, grid):
if grid[i][j] == 1: return i
if grid[i][j] == 0: return j
def champion_team(grid):
best = []
my_max = 0
for team in range(len(grid)):
my_sum = sum(grid[team])
if my_sum > my_max:
best = [team]
my_max = my_sum
elif my_sum == my_max:
best.append(team)
if len(best) == 1:
return best[0]
i, j = best
return stronger(i, j, grid)
View the entire Python script for this task on GitHub.
Elixir
Whenever I need a loop in Elixir, it’s done either through recursion or through Enum.map_reduce/3
. I was quite pleased with how easily I was able to deal with the best
and max
variables in a single line for each case on lines 15, 17, and 19, where I needed two lines for the equivalent in the other languages.
def stronger(i, j, grid) do
cond do
grid |> Enum.at(i) |> Enum.at(j) == 1 -> i
grid |> Enum.at(i) |> Enum.at(j) == 0 -> j
end
end
def reduce_fn(row, {best, max, team}) do
sum = Enum.sum(row)
{best, max} = cond do
sum > max ->
{[team], sum}
sum == max ->
{best ++ [team], max}
true ->
{best, max}
end
{row, {best, max, team+1}}
end
def champion_team(grid) do
{_, {best, _, _}} =
Enum.map_reduce(grid, {[], 0, 0}, &reduce_fn/2)
if length(best) == 1 do
best |> List.first
else
stronger(List.first(best), List.last(best), grid)
end
end
View 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-343/packy-anderson