23 February 2024 | Challenge 257 |
Ranked Echelons
Task 1: Smaller than Current
Submitted by: Mohammad Sajid Anwar
You are given a array of integers, @ints.
Write a script to find out how many integers are smaller than current i.e. foreach ints[i]
, count ints[j] < ints[i]
where i != j
.
Example 1
Input: @ints = (5, 2, 1, 6)
Output: (2, 1, 0, 3)
For $ints[0] = 5, there are two integers (2,1) smaller than 5.
For $ints[1] = 2, there is one integer (1) smaller than 2.
For $ints[2] = 1, there is none integer smaller than 1.
For $ints[3] = 6, there are three integers (5,2,1) smaller than 6.
Example 2
Input: @ints = (1, 2, 0, 3)
Output: (1, 2, 0, 3)
Example 3
Input: @ints = (0, 1)
Output: (0, 1)
Example 4
Input: @ints = (9, 4, 9, 2)
Output: (2, 1, 2, 0)
Solution
This task is the same as task 1 in challenge 244. Looking for a different approach.
At first let’s assume the list elements were unique.
Considering example 2. We start with an index sort on the list.
L: [1, 2, 0, 3] => IS: [2, 0, 1, 3]
We may read this as: The element L[IS[i]]
is the i
-th element in the sorted list and thus there are i
elements smaller than L[IS[i]]
.
Pairing the elements in IS
with their index yields:
[
[2, 0],
[0, 1],
[1, 2],
[3, 3]
]
Sorting by the first column reveals the requested result in the second column:
[
[0, 1],
[1, 2],
[2, 0],
[3, 3]
]
Obviously this fails, when the elements are not unique, as equal values have different positions in the sorted list.
This may be compensated by a “local enumeration”, where equal values in a sorted list are assigned a sequence number.
PDL
has a function enumvec
that performs just this task.
So with PDL
we may write this as:
use PDL;
use PDL::NiceSlice;
sub stc {
my $l = long @_;
my $si = $l->qsorti;
cat($si, sequence($l) - $l->($si)->dummy(0)->enumvec)
->xchg(0,1)->qsortvec->((1));
}
See the full solution.
Task 2: Reduced Row Echelon
Submitted by: Ali Moradi
Given a matrix M
, check whether the matrix is in reduced row echelon form.
A matrix must have the following properties to be in reduced row echelon form:
- If a row does not consist entirely of zeros, then the first nonzero number in the row is a 1. We call this the leading 1.
- If there are any rows that consist entirely of zeros, then they are grouped together at the bottom of the matrix.
- In any two successive rows that do not consist entirely of zeros, the leading 1 in the lower row occurs farther to the right than the leading 1 in the higher row.
- Each column that contains a leading 1 has zeros everywhere else in that column.
For example:
[
[1,0,0,1],
[0,1,0,2],
[0,0,1,3]
]
The above matrix is in reduced row echelon form since the first nonzero number in each row is a 1, leading 1s in each successive row are farther to the right, and above and below each leading 1 there are only zeros.
For more information check out this [wikipedia}(https://en.wikipedia.org/wiki/Row_echelon_form) article.
Example 1
Input: $M = [
[1, 1, 0],
[0, 1, 0],
[0, 0, 0]
]
Output: 0
Example 2
Input: $M = [
[0, 1,-2, 0, 1],
[0, 0, 0, 1, 3],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
Output: 1
Example 3
Input: $M = [
[1, 0, 0, 4],
[0, 1, 0, 7],
[0, 0, 1,-1]
]
Output: 1
Example 4
Input: $M = [
[0, 1,-2, 0, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 1, 3],
[0, 0, 0, 0, 0]
]
Output: 0
Example 5
Input: $M = [
[0, 1, 0],
[1, 0, 0],
[0, 0, 0]
]
Output: 0
Example 6
Input: $M = [
[4, 0, 0, 0],
[0, 1, 0, 7],
[0, 0, 1,-1]
]
Output: 0
Solution
There are some tests to be performed on the given matrix.
- find all ones in the matrix and get their indices
- limit the index list to those elements that represent the first
1
in a row. - check that the columns from the “first ones” are strictly ascending
- check that are no nonzero elements before and above each of the “first ones”
1
s - identify all zero rows
- check that the last row having a leading
1
comes before the first zero row - check that the number of rows with a leading
1
plus the number of zero rows matches the total number of rows - if all these checks have succeeded, the matrix is in reduced echelon form.
As Perl/PDL code:
use PDL;
use PDL::NiceSlice;
sub is_reduced_echelon {
my $m = pdl @_;
my $allones = whichND($m == 1);
my $firstidx = which($allones->(1)->enumvec == 0);
my $firstones = $allones->dice('X', $firstidx);
return unless $firstones->dim(1) < 2 ||
all $firstones((0),0:-2) < $firstones((0),1:-1);
for my $firstone ($firstones->dog) {
my ($col, $row) = $firstone->list;
return unless 1 == sum $m->dice('X', $row)->(0:$col) != 0;
return unless 1 == sum $m->dice($col, 'X')->(,0:$row) != 0;
}
my $zeros = which $m->orover == 0;
return unless !$firstones->ngood || !$zeros->ngood ||
$firstones->((1))->max < $zeros->min;
return unless $zeros->dim(0) + $firstidx->dim(0) == $m->dim(1);
1;
}
See the full solution.
If you have a question about this post or if you like to comment on it, feel free to open an issue in my github repository.