You didn’t know what the Weekly Challenge was / Until you met a hacker on a Github bus / I got there in the nick of time / Before he got his code across your git commit line…
Yeah, yeah, it’s a stretch, but it’s an excuse to pull out Ian Hunter’s Once Bitten, Twice Shy (yes, Ian Hunter).
So you got the rhythm, you got the speed, you got Perl Weekly Challenge 292.
(sorry I’m only doing one challenge this week; it’s been a rough week)
Task 1: Twice Largest
You are given an array of integers, @ints
, where the largest integer is unique
.
Write a script to find whether the largest element in the array is at least twice
as big as every element in the given array. If it is return the index of the largest element or return -1
otherwise.
Example 1
Input: @ints = (2, 4, 1, 0)
Output: 1
The largest integer is 4.
For every other elements in the given array is at least twice as big.
The index value of 4 is 1.
Example 2
Input: @ints = (1, 2, 3, 4)
Output: -1
The largest integer is 4.
4 is less than twice the value of 3, so we return -1.
Approach
Part of me wants to do this in a single pass through the array. To do that, I think I’d need to maintain two variables besides the index: a largest value (largest
) and a second largest (second
) value. There’s a trivial case where the array is one element long, which cannot meet the “at least twice
as big as every [other] element in the given array” part of the criterial.
So if there’s more than 1 element in the array, we start with largest
equal to the index of the first element and second
equal to -1
(which can’t be an array index). Then we process the rest of the list; if the value at the current index is larger than the value at index largest
, we push the index in largest
down to second
and put the current index in largest
, if the value at the current index is smaller than the value at index largest
but larger than the value at index second
(or the index of second
is -1
), just replace second
with the current index. Then, once we’ve processed all the values in the array, if ints[largest] >= 2 * ints[second]
, we return largest
, otherwise we return -1
.
Raku
This is pretty much what I wrote above, except in Raku.
sub twiceLargest(@ints) {
return -1 if @ints.elems <= 1;
my $largest = 0;
my $second = -1;
for 1 .. @ints.end -> $i {
if (@ints[$i] > @ints[$largest]) {
$second = $largest;
$largest = $i;
}
elsif ($second < 0 || @ints[$i] > @ints[$second]) {
$second = $i;
}
}
return @ints[$largest] >= 2 * @ints[$second] ?? $largest !! -1;
}
View the entire Raku script for this task on GitHub.
$ raku/ch-1.raku
Example 1:
Input: @ints = (2, 4, 1, 0)
Output: 1
Example 2:
Input: @ints = (1, 2, 3, 4)
Output: -1
Example 3:
Input: @ints = (1)
Output: -1
Example 4:
Input: @ints = (4, 2, 1)
Output: 0
Perl
Translating to Perl doesn’t change much.
sub twiceLargest(@ints) {
return -1 if @ints <= 1;
my $largest = 0;
my $second = -1;
for my $i ( 1 .. $#ints ) {
if ($ints[$i] > $ints[$largest]) {
$second = $largest;
$largest = $i;
}
elsif ($second < 0 || @ints[$i] > @ints[$second]) {
$second = $i;
}
}
return $ints[$largest] >= 2 * $ints[$second] ? $largest : -1;
}
View the entire Perl script for this task on GitHub.
Python
And translating into Python doesn’t change much.
def twiceLargest(ints):
if len(ints) <= 1:
return -1
largest = 0
second = -1
for i in range(1, len(ints)):
if ints[i] > ints[largest]:
second = largest
largest = i
elif second < 0 or ints[i] > ints[second]:
second = i
return largest if ints[largest] >= 2 * ints[second] else -1
View the entire Python script for this task on GitHub.
Elixir
I’m using a recursive traverseList/5
function to simulate looping over the list of ints by index, and having it return values for largest
and second
.
def traverseList([], _, _, largest, second), do:
{largest, second}
def traverseList([val | rest], i, ints, largest, second) do
{largest, second} = cond do
val >= Enum.at(ints, largest) ->
second = largest
largest = i
{largest, second}
second == -1 || val > Enum.at(ints, second) ->
second = i
{largest, second}
true -> {largest, second}
end
traverseList(rest, i+1, ints, largest, second)
end
def twiceLargest(ints) when length(ints) <= 1, do: -1
def twiceLargest(ints) do
{_, rest} = List.pop_at(ints, 0) # remove the first element
{largest, second} = traverseList(rest, 1, ints, 0, -1)
if Enum.at(ints, largest) >= 2 * Enum.at(ints, second) do
largest
else
-1
end
end
View the entire Elixir script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-292/packy-anderson