I’ve been doing the Weekly
Challenges. The
latest
involved a binary negation and list division. (Note that this is open
until 27 November 2022.)
Task 1: Binary Flip
You are given a positive integer, $n
.
Write a script to find the binary flip.
This turns out to be: take the binary representation (with no leading
zeroes), then exchange all 0s and 1s to get an inverse value.
This is clearly intended to be a problem about binary representations.
But we don't need to do that. Instead, I find the value of the
leftmost set bit in the input by counting repeated shift rights until
there's nothing left – e.g. 5 is processed as 101, 10, 1, 0
, so
three shifts. (I.e. log base 2.) Then shift 1 left that many times
(getting 8). Subtract 1: 7, which gives me what in binary would be a
series of "1" of the same length as the original number. Then subtract
the input from that (gosh, an actual legitimate "take away the number
you first thought of").
This sequence is in the OEIS and the graph
makes this approach particularly obvious.
Some languages have access to CPU functions to count leading zeroes
and thus get a binary magnitude that way, but I did this the same way
throughout. JavaScript:
function binaryflip(n) {
let m = n;
let r = 0;
while (m > 0) {
m >>= 1;
r += 1;
}
return (1 << r) - 1 - n;
}
Task 2: Equal Distribution
You are given a list of integers greater than or equal to zero, @list
.
Write a script to distribute the number so that each members are
same. If you succeed then print the total moves otherwise print -1.
"Distribute" turns out to mean "subtract one from a list value, adding
it to another adjacent list value". So this has to be done iteratively
[0 3 0]
→ [1 2 0]
→ [1 1 1]
= 2 steps , adding and subtracting 1
each time. If a number is higher than both its neighbours, the process
is not immediately obvious, but this one seemed to work.
Raku:
sub equaldistribution(@list) {
Check that an even distribution is possible.
my $s = @list.sum;
if ($s % @list.elems != 0) {
return -1;
}
Get a target value for each element.
my $m = $s div @list.elems;
my $out = 0;
my @w = @list;
while (True) {
for (0..@w.elems-2) -> $i {
Going through the list, if this value is higher than it needs to be,
move any surplus into the next element.
if (@w[$i] > $m) {
my $v = @w[$i] - $m;
@w[$i+1] += $v;
$out += $v;
@w[$i] = $m;
If it's lower, take as much as possible from the next element (without
letting it go below zero.)
} elsif (@w[$i] < $m) {
my $v = min($m - @w[$i], @w[$i+1]);
@w[$i+1] -= $v;
$out += $v;
@w[$i] += $v;
}
}
If the job's done, exit, otherwise loop again.
if (@w.all == $m) {
last;
}
}
return $out;
}
Full code on
github.