Arranged Average
with Raku

by Arne Sommer

Arranged Average with Raku

[325] Published 19. January 2025.

This is my response to The Weekly Challenge #304.

Challenge #304.1: Arrange Binary

You are given a list of binary digits (0 and 1) and a positive integer, $n.

Write a script to return true if you can re-arrange the list by replacing at least $n digits with 1 in the given list so that no two consecutive digits are 1 otherwise return false.

Example 1:
Input: @digits = (1, 0, 0, 0, 1), $n = 1
Output: true

Re-arranged list: (1, 0, 1, 0, 1)
Example 2:
Input: @digits = (1, 0, 0, 0, 1), $n = 2
Output: false
File: arrange-binary
#! /usr/bin/env raku

unit sub MAIN (*@ints is copy where @ints.elems > 0
                               && all(@ints) eq any(0,1),           # [1]
               Int :$n where $n > 0,                                # [2]
               :v(:$verbose));

if @ints.join ~~ /11/                                               # [3]
{
  say ':Found neighbourly 1s in the input' if $verbose;
  say 'false';                                                      # [3a]
  exit;                                                             # [3b]
}

my $replaced = 0;                                                   # [4]

for 0 .. @ints.end -> $index                                        # [5]
{
  if @ints[$index] == 0                                             # [6]
  {
    if $index == 0 || @ints[$index -1] == 0                         # [7]
    {
      if $index == @ints.end || @ints[$index +1] == 0               # [8]
      {
        @ints[$index] = 1;                                          # [9]
        $replaced++;                                                # [10]

        say ": Replaced #$replaced at index $index: [{ @ints.join(",") }]"
          if $verbose;
      }
    }
  }
}

say $replaced >= $n ?? 'true' !! 'false';                           # [11]

[1] At least one element, all of which must be either 0 or 1. This is set up as an any junction. Note the is copy so that the program can change the values (in [9]).

See docs.raku.org/routine/any for more information about any.

See docs.raku.org/type/Parameter#method_copy for more information about is copy.

[2] A named argument for the $n value, which must be a positive integer.

[3] We are not allowed to have two consecutive 1s, and should check that this is not the case initially. In which case we cannot replace anything.

[4] The number of replacements will end up here.

[5] Iterate over the indices in the input. end gives the index of the last item. (for ^@ints -> $index would do the same, and is shorter.)

See docs.raku.org/routine/end for more information about end.

[6] Is the current value zero?

[7] Is the current index zero (i.e. no left value) or the value to the left is also zero?

[8] Is the current index the end or the value to the right is also zero?

[9] Replace the current zero with one.

[10] Increase the count.

[11] Did we manage to replace at least $n zeroes?.

Running it:

$ ./arrange-binary -n=1 1 0 0 0 1
true

$ ./arrange-binary -n=2 1 0 0 0 1
false

Looking good.

With verbose mode:

$ ./arrange-binary -n=1 -v 1 0 0 0 1
: Replaced #1 at index 2: [1,0,1,0,1]
true

$ ./arrange-binary -n=2 -v 1 0 0 0 1
: Replaced #1 at index 2: [1,0,1,0,1]
false

Challenge #304.2:

You are given an array of integers, @ints and an integer, $n which is less than or equal to total elements in the given array.

Write a script to find the contiguous subarray whose length is the given integer, $n, and has the maximum average. It should return the average.

Example 1:
Input: @ints = (1, 12, -5, -6, 50, 3), $n = 4
Output: 12.75

Subarray: (12, -5, -6, 50)
Average: (12 - 5 - 6 + 50) / 4 = 12.75
Example 2:
Input: @ints = (5), $n = 1
Output: 5
File: maximum-average
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int,  # [1]
               Int :$n where $n > 0 && $n <= @ints.elems,          # [2]
               :v(:$verbose));

my $max-avg = -Inf;                                                # [3]

for 0 .. @ints.elems - $n -> $start                                # [4]
{
  my $avg = @ints[$start .. $start + $n -1].sum / $n;              # [5]

  if $avg > $max-avg                                               # [6]
  {
    $max-avg = $avg;                                               # [7]

    say ": New maximum average $max-avg at index $start to \
      { $start + $n -1 }" if $verbose;
  }
  elsif $verbose
  {
    say ": Not a new maximum average $max-avg at index $start to \
      { $start + $n -1 }";
  }
}

say $max-avg;                                                     # [8]

[1] At least one element, all of which must be integers.

[2] A named integer argument, which must be in the range 1 .. number of elements in @ints.

[3] The maximum value, starting at -Inf - the absolutely lowest value possible.

[4] Iterate over the starting positions of the possible subarrays. Note the -$n so that the last one is long enough (has length $c.)

[5] Get the average of the subarray. Note the use of sum to add the entire subarray together.

See docs.raku.org/routine/sum for more information about sum.

[6] Do we have a higher average than the previous one?

[7] Set the new average.

[8] Print the average value.

Running it:

$ ./maximum-average -n=4 1 12 -5 -6 50 3
12.75

$ ./maximum-average -n=1 5
5

Looking good.

With verbose mode:

$ ./maximum-average -n=4 -v 1 12 -5 -6 50 3
: New maximum average 0.5 at index 0 to 3
: New maximum average 12.75 at index 1 to 4
: Not a new maximum average 12.75 at index 2 to 5
12.75

$ ./maximum-average -n=1 -v 5
: New maximum average 5 at index 0 to 0
5

And that's it.