Perl Weekly Challenge: Ze-ro the CHAMPIONS!

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

Leave a Reply