I’ve been doing the Weekly
Challenges. The
latest
involved list processing and matrix testing. (Note that this ends today.)
Task 1: Smaller than Current
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
.
There's an obvious O(n²) way of doing this, but I thought it was
cleaner to sort.
Scala:
def smallerthancurrent(a: List[Int]): List[Int] = {
Set up a sorted list of inputs, and a map that'll contain the solution.
val s: List[Int] = a.sortWith(_ < _)
var l = mutable.Map.empty[Int, Int]
For each item in the sorted list, store the index, which is the number
of items smaller than that. (Use the first occurrence of the item; I
added the test case with a duplicate value.)
for ((n, i) <- s.zipWithIndex) {
if (!l.contains(n)) {
l += (n -> i)
}
}
Then just read off the values in the order of the original list.
a.map { n => l(n) }.toList
}
Task 2: Reduced Row Echelon
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.
I propsed a bunch of the test cases here because I wanted to test each
rule individually. Raku:
sub reducedrowechelon(@a) {
We'll be using the position of the first 1 in each row quite a bit.
That goes in @leadingone
.
my @leadingone;
Inspect each row:
for @a -> @row {
Set the leading-one position to -1, indiciating no 1 found.
my $lp = -1;
For each item in the row:
for 0 .. @row.end -> $cn {
my $cell = @row[$cn];
If it's a 1, store that position and exit.
if ($cell == 1) {
$lp = $cn;
last;
If it's not a zero, bail out (rule 1).
} elsif ($cell != 0) {
return False;
}
}
Add this row's leading-one position to the list.
@leadingone.push($lp);
}
If there are trailing rows of all-zero, ignore them.
while (@leadingone[*-1] == -1) {
@leadingone.pop;
}
Now sort the remaining leading-one positions.
my @c = @leadingone.sort({$^a <=> $^b});
If there are any all-zero rows, they weren't trailing. Bail out by
rule 2.
if (@c[0] == -1) {
return False;
}
If the sorted list doesn't match the original, the leading ones aren't
progressively to the right. Bail out by rule 3.
if (!(@c eqv @leadingone)) {
return False;
}
Finally take a columnwise slice of each column that has a leading 1 in
it. Test for 1s and 0s in the right places (rule 4).
for @c -> $i {
my @col = @a.map({$_[$i]}).sort({$^a <=> $^b});
if (@col[*-1] != 1 ||
@col[*-2] != 0 ||
@col[0] != 0) {
return False;
}
}
return True;
}
I spot two possible problems here:
(1) the rule-3 test will pass two leading-ones of equal value. But the
rule-4 test will remove those because they won't be unique in the
column. I'd rather rule 3 be fully tested in its own segment of the
codex.
(2) the rule-4 test should reject a negative value found in the same
column as a leading 1 (@col[0] != 0
above). The existing test cases
don't fail in that case (this is my fault since I proposed most of
them).
Full code on
github.
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.