Perl Weekly Challenge: Week 327

Challenge 1:

Missing Integers

You are given an array of n integers.

Write a script to find all the missing integers in the range 1..n in the given array.

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

The given array has 6 elements.
So we are looking for integers in the range 1..6 in the given array.
The missing integers: (4, 6)
Example 2
Input: @ints = (1, 1, 1)
Output: (2, 3)
Example 3
Input: @ints = (2, 2, 1)
Output: (3)

This is really a problem of sets. We have one set which contains all the integers from 1 to the size of the second set (no duplicates.) We are given another set of integers (some of which could be duplicates.) We need to find the elements of set B which are not in set A; in other words, the set difference. Fortunately Raku has a wide selection of operators for sets including a set difference operator, . Most of the work then is just getting the two sets assembled.

The first set is just a Sequence created by the range operator .. from 1 to the size of the input i.e. @ints.elems.

The second set is taken from the command-line arguments which are an Array of Strings, so we convert them back to integers with ».Int. Then duplicates are removed via the .unique() method.

The operator is applied to both of these which creates a result of type Set. To get the actual missing numbers out of this set, we use .keys() and, just for show, we sort the numbers in numeric order with .sort().

my @missing = ((1.. @ints.elems) ∖ @ints».Int.unique).keys.sort({$^a <=> $^b});

The second line prints out the missing numbers with say(). The rest of the code on this line is just to print the result in the format of the spec.

say q{(}, @missing.join(q{, }), q{)};

(Full code on Github.)

For Perl, I had to once again reach into my bag of code from previous challenges to provide replacements for unique() and set difference (the setDifference() function.) The rest works the same as in Raku.

my @missing = sort { $a <=> $b } setDifference([1 .. scalar @ints], unique(@ints));
say q{(}, (join q{, }, @missing), q{)};

(Full code on Github.)

Challenge 2:

MAD

You are given an array of distinct integers.

Write a script to find all pairs of elements with minimum absolute difference (MAD) of any two elements.

Example 1
Input: @ints = (4, 1, 2, 3)
Output: [1,2], [2,3], [3,4]

The minimum absolute difference is 1.
Pairs with MAD: [1,2], [2,3], [3,4]
Example 2
Input: @ints = (1, 3, 7, 11, 15)
Output: [1,3]
Example 3
Input: @ints = (1, 5, 3, 8)
Output: [1,3], [3,5]

We define a Hash whose keys will be absolute differences and values the pairs which have that particular difference.

my %differences;

%differences is populated in this loop. As in challenge 1, input taken from the command-line has to be converted back into integers with ».Int then .combinations(2) gives us all pairs of those integers.

for @ints».Int.combinations(2) -> $pair {

One value of each pair is subtracted from the other (using [-] as a shortcut) and the absolute value of the result is taken with .abs(). This is the absolute difference so the pair is added to %differences under that key.

    %differences{([-] @$pair).abs}.push($pair);
}

Now if we look at the keys of %differences (which are strings so they have to be converted into integers again) we can find the minimum with .min().

%differences{%differences.keys».Int.min}

We use .map() to take each pair under that key and transform then into strings where the elements of the pair are in numeric order and bookended with []. This is just so the output looks like that in the spec.

    .map({ "[@$_.sort({ $^a <=> $^b }).join(q{,})]" })

The pairs are sorted; again for presentation purposes.

    .sort

This one is for looks too.

    .join(q{, })

Finally, when we have everything looking right, we print it out with .say().

    .say;

(Full code on Github.)

The Perl version needs us to provide combinations() but otherwise is similar to Raku. It is considerably harder to read in my opinion.

my %differences;

for my $pair (combinations(\@ints, 2)) {
    push @{ $differences{ abs($pair->[0] - $pair->[1]) } }, $pair;
}

say
join q{, },
sort
map { q{[} . ( join q{,}, sort { $a <=> $b } @{$_} ) . q{]} }
@{ $differences{ (sort { $a <=> $b } keys %differences)[0] } };

(Full code on Github.)