Ones by 1
with Raku

by Arne Sommer

Ones by 1 with Raku

[291] Published 30. May 2024.

This is my response to The Weekly Challenge #271.

Challenge #271.1: Maximum Ones

You are given a m x n binary matrix.

Write a script to return the row number containing maximum ones, in case of more than one rows then return smallest row number.

Example 1:
Input: $matrix = [ [0, 1],
                   [1, 0],
                 ]
Output: 1

Row 1 and Row 2 have the same number of ones, so return row 1.
Example 2:
Input: $matrix = [ [0, 0, 0],
                   [1, 0, 1],
                 ]
Output: 2

Row 2 has the maximum ones, so return row 2.
Example 3:
Input: $matrix = [ [0, 0],
                   [1, 1],
                   [0, 0],
                 ]
Output: 2

Row 2 have the maximum ones, so return row 2.

File: maximum-ones
#! /usr/bin/env raku

unit sub MAIN ($string = "0 0 | 1 1 | 0 0", :v(:$verbose));             # [1]

my $matrix = $string.split("|")>>.words>>.Int>>.Array;                  # [2]

die "The rows must have the same size" unless [==] $matrix>>.elems;     # [3]

die "Must contain 0s and 1s only" unless all($matrix[*;*]) ~~ one(0,1); # [4]

my @sum = $matrix>>.sum.pairs                                           # [5]
     .sort({ $^b.value <=> $^a.value || $^a.key <=> $^b.key });         # [5a]

say ": index => sums: { @sum.raku } (zero based index)" if $verbose;

say 1 + @sum[0].key;                                                    # [6]

[1] Note the default matrix, and the syntax for specifying it as a string.

[2] Convert the matrix-as-a-string thingy to a two dimentional array.

[3] Ensure that all the rows have the same length.

[4] Retrieve all the values and ensure that all of them are oither 0 or 1.

[5] Apply sum on each row to reduce the rows to a single value. Then turn the sums into an array of Pair objects with pairs. The key is the index from the former array, and the value is the former value itself. Then we sort this array, on the value part (highest first), and then if the values are equal on the key (row number; lowest first).

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

[6] Get the first result, and add 1 to the index as the challenge uses 1 for the first row (and Raku is zero based).

Running it:

$ ./maximum-ones "0 1 | 1 0"
1

$ ./maximum-ones "0 0 0 | 1 0 1"
2

$ ./maximum-ones 
2

Looking good.

With verbose mode:

$ ./maximum-ones -v "0 1 | 1 0"
: index => sums: [0 => 1, 1 => 1] (zero based index)
1

$ ./maximum-ones -v "0 0 0 | 1 0 1"
: index => sums: [1 => 2, 0 => 0] (zero based index)
2

$ ./maximum-ones -v 
: index => sums: [1 => 2, 0 => 0, 2 => 0] (zero based index)
2

Challenge #271.2: Sort by 1 bits

You are give an array of integers, @ints.

Write a script to sort the integers in ascending order by the number of 1 bits in their binary representation. In case more than one integers have the same number of 1 bits then sort them in ascending order.

Example 1:
Input: @ints = (0, 1, 2, 3, 4, 5, 6, 7, 8)
Output: (0, 1, 2, 4, 8, 3, 5, 6, 7)

0 = 0 one bits
1 = 1 one bits
2 = 1 one bits
4 = 1 one bits
8 = 1 one bits
3 = 2 one bits
5 = 2 one bits
6 = 2 one bits
7 = 3 one bits
Example 2:
Input: @ints = (1024, 512, 256, 128, 64)
Output: (64, 128, 256, 512, 1024)

All integers in the given array have one 1-bits, so just sort them in ascending order.

The challenge does not say anything about duplicate values, so I have chosen to allow it.

File: sort-by-1-bits
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems > 0, :v(:$verbose));  # [1]

die "Non-negative integers only" unless all(@ints) ~~ UInt;   # [2]


my @binary = @ints.map({ $_.fmt('%b').comb.sum => $_ })       # [3]
  .sort({$^a.key <=> $^b.key || $^a.value <=> $^b.value });   # [3a]

@binary.map({ say ": { $_.value } = { $_.key } one bits" }) if $verbose;

say '(', @binary.map( *.value ).join(", "), ')';              # [4]

[1] Ensure at least on element.

[2] They must all be unsigned integers.

[3] {ID:fmt}} The map part converts the value to binary (with fmt), them we split that number into individual characters (with comb), and add them all together with sum - thus giving the number of 1 bits. That is on the left hand side of the Pair (created with the => thing). The value itself is the value of the pair. Then we sort the pairs.

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

[4] Use map to convert the pair objects into the value part, and pritty print the result.

Running it:

$ ./sort-by-1-bits 0 1 2 3 4 5 6 7 8
(0, 1, 2, 4, 8, 3, 5, 6, 7)

$ ./sort-by-1-bits 1024 512 256 128 64
(64, 128, 256, 512, 1024)

Looking good.

With verbose mode:

$ ./sort-by-1-bits -v 0 1 2 3 4 5 6 7 8
: 0 = 0 one bits
: 1 = 1 one bits
: 2 = 1 one bits
: 4 = 1 one bits
: 8 = 1 one bits
: 3 = 2 one bits
: 5 = 2 one bits
: 6 = 2 one bits
: 7 = 3 one bits
(0, 1, 2, 4, 8, 3, 5, 6, 7)

$ ./sort-by-1-bits -v 1024 512 256 128 64
: 64 = 1 one bits
: 128 = 1 one bits
: 256 = 1 one bits
: 512 = 1 one bits
: 1024 = 1 one bits
(64, 128, 256, 512, 1024)

And that's it.