Perl Weekly Challenge 327.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 327.
Task 1: Missing Integers
Submitted by: Mohammad Sajid Anwar
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)
I can use a hash with the present integers as keys, and use it to
grep
the missing ones. This yields a simple oneliner.
Example 1:
perl -E '
$p{$_}++ for @ARGV; say "@ARGV -> ", join " ", grep {!$p{$_}} 1..@ARGV
' 1 2 1 3 2 5
Results:
1 2 1 3 2 5 -> 4 6
Example 2:
perl -E '
$p{$_}++ for @ARGV; say "@ARGV -> ", join " ", grep {!$p{$_}} 1..@ARGV
' 1 1 1
Results:
1 1 1 -> 2 3
Example 3:
perl -E '
$p{$_}++ for @ARGV; say "@ARGV -> ", join " ", grep {!$p{$_}} 1..@ARGV
' 2 2 1
Results:
2 2 1 -> 3
The corresponding full code is:
1 # Perl weekly challenge 327
2 # Task 1: Missing Integers
3 #
4 # See https://wlmb.github.io/2025/06/26/PWC327/#task-1-missing-integers
5 use v5.36;
6 die <<~"FIN" unless @ARGV;
7 Usage: $0 N1 N2..Nm
8 to find the numbers 1..m missing from N1 N2..Nm
9 FIN
10 my %present;
11 $present{$_}++ for @ARGV;
12 say "@ARGV -> ", join " ", grep {!$present{$_}} 1..@ARGV
Example:
./ch-1.pl 1 2 1 3 2 5
./ch-1.pl 1 1 1
./ch-1.pl 2 2 1
Results:
1 2 1 3 2 5 -> 4 6
1 1 1 -> 2 3
2 2 1 -> 3
Task 2: MAD
Submitted by: Mohammad Sajid Anwar
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]
I keep an array of pairs of integers distant by the minimum distance so far. I throw it away and continue with a new minimum whenever I find a still smaller distance. The result fits a two liner.
Example 1:
perl -E '
@a=@ARGV;$m=abs($a[1]-$a[0]);for $i(0..@a-1){for $j($i+1..@a-1){$d=abs(($x=$a[$i])-($y=$a[$j]));
$m=$d,@r=() if $d<$m;push @r,[$x,$y] if $d==$m;}}say "[@a] -> [", (map{"[@$_]"} @r),"]"
' 4 1 2 3
Results:
[4 1 2 3] -> [[4 3][1 2][2 3]]
Example 2:
perl -E '
@a=@ARGV;$m=abs($a[1]-$a[0]);for $i(0..@a-1){for $j($i+1..@a-1){$d=abs(($x=$a[$i])-($y=$a[$j]));
$m=$d,@r=() if $d<$m;push @r,[$x,$y] if $d==$m;}}say "[@a] -> [", (map{"[@$_]"} @r),"]"
' 1 3 7 11 15
Results:
[1 3 7 11 15] -> [[1 3]]
Example 3:
perl -E '
@a=@ARGV;$m=abs($a[1]-$a[0]);for $i(0..@a-1){for $j($i+1..@a-1){$d=abs(($x=$a[$i])-($y=$a[$j]));
$m=$d,@r=() if $d<$m;push @r,[$x,$y] if $d==$m;}}say "[@a] -> [", (map{"[@$_]"} @r),"]"
' 1 5 3 8
Results:
[1 5 3 8] -> [[1 3][5 3]]
Notice that I don’t really require the input numbers to be integer nor distinct.
The corresponding full code is similar:
1 # Perl weekly challenge 327
2 # Task 2: MAD
3 #
4 # See https://wlmb.github.io/2025/06/26/PWC327/#task-2-mad
5 use v5.36;
6 die <<~"FIN" unless @ARGV>=2;
7 Usage: $0 N1 N2...
8 to find the pair of integers Ni Nj separated by
9 the minimum absolute difference.
10 FIN
11 my $minimum=abs($ARGV[1]-$ARGV[0]);
12 my @result;
13 for my $i(0..@ARGV-1){
14 my $x=$ARGV[$i];
15 for my $j($i+1..@ARGV-1){
16 my $y=$ARGV[$j];
17 my $distance=abs($x-$y);
18 $minimum=$distance, @result=() if $distance < $minimum;
19 push @result, $x<$y? [$x,$y]:[$y,$x] if $distance==$minimum;
20 }
21 }
22 say "[@ARGV] -> [", (map{"[@$_]"} @result),"]"
23
Example:
./ch-2.pl 4 1 2 3
./ch-2.pl 1 3 7 11 15
./ch-2.pl 1 5 3 8
Results:
[4 1 2 3] -> [[3 4][1 2][2 3]]
[1 3 7 11 15] -> [[1 3]]
[1 5 3 8] -> [[1 3][3 5]]
An alternative is to use the Perl Data language PDL. I build an ndarray with the absolute value of the differences, find the minimum nonzero value, select the pairs for which the difference equals this minimum and remove equivalent pairs. The result also fits a two liner.
Examples:
perl -MPDL -MPDL::NiceSlice -E '
for(@ARGV){$n=($p=pdl($_))->nelem;$r=$p->("*".$n);$q=($r-$p)->abs;$m=$q->where($q>0)->min;
$s=pdl($r,$r->mv(0,1))->whereND($q==$m);say "$p -> ",$s->whereND($s(:,0)<$s(:,1))->mv(0,1);}
' '[4 1 2 3]' '[1 3 7 11 15]' '[1 5 3 8]'
Results:
[4 1 2 3] ->
[
[1 2]
[2 3]
[3 4]
]
[1 3 7 11 15] ->
[
[1 3]
]
[1 5 3 8] ->
[
[1 3]
[3 5]
]
The corresponding full code is the following
1 # Perl weekly challenge 327
2 # Task 2: MAD
3 #
4 # PDL version
5 # See https://wlmb.github.io/2025/06/26/PWC327/#task-2-mad
6 use v5.36;
7 use PDL;
8 use PDL::NiceSlice;
9 die <<~"FIN" unless @ARGV>=2;
10 Usage: $0 A1 A2...
11 to find distinct pairs of integers [Ni Nj] taken from the array
12 Ap=[N1 N2...] separated by the minimum absolute difference.
13 The input should be in the form of strings "[N1 N2...]" which may
14 be interpreted as ndarrays by PDL.
15 FIN
16 for(@ARGV){
17 my $p=pdl($_); # 1D array
18 say("Expected a 1D array: $_"), next unless $p->ndims==1;
19 my $n=$p->nelem;
20 say("Expected two or more elements: $_"), next unless $n>1;
21 my $pp=$p->dummy(0,$n); # 2D array
22 my $ad=($pp-$p)->abs; # abs difference
23 my $min=$ad->where($ad>0)->min; # MAD
24 my $pairs=pdl($pp,$pp->mv(0,1))->whereND($ad==$min); # select pairs
25 say "$p -> ",$pairs->whereND($pairs(:,0)<$pairs(:,1))->mv(0,1); # output distinct
26 }
Examples:
./ch-2a.pl '[4 1 2 3]' '[1 3 7 11 15]' '[1 5 3 8]'
Results:
[4 1 2 3] ->
[
[1 2]
[2 3]
[3 4]
]
[1 3 7 11 15] ->
[
[1 3]
]
[1 5 3 8] ->
[
[1 3]
[3 5]
]
/;