Perl Weekly Challenge 248.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 248.
Task 1: Shortest Distance
Submitted by: Mohammad S Anwar
You are given a string and a character in the given string.
Write a script to return an array of integers of size same as length of the
given string such that:
distance[i] is the distance from index i to the closest occurence of
the given character in the given string.
The distance between two indices i and j is abs(i - j).
Example 1
Input: $str = "loveleetcode", $char = "e"
Output: (3,2,1,0,1,0,0,1,2,2,1,0)
The character 'e' appears at indices 3, 5, 6, and 11 (0-indexed).
The closest occurrence of 'e' for index 0 is at index 3, so the distance
is abs(0 - 3) = 3.
The closest occurrence of 'e' for index 1 is at index 3, so the distance
is abs(1 - 3) = 2.
For index 4, there is a tie between the 'e' at index 3 and the 'e' at
index 5, but the distance is still the same: abs(4 - 3) == abs(4 - 5) = 1.
The closest occurrence of 'e' for index 8 is at index 6, so the distance is
abs(8 - 6) = 2.
Example 2
Input: $str = "aaab", $char = "b"
Output: (3,2,1,0)
This can be solved by making an array of the indices corresponding to the letter and treat it as the row of a matrix. I take all the indices corresponding to the string and treat them as a column of another matrices. I repeat the rows and the columns to make compatible matrices which I can subtract, take the absolute values, and finally, I take the minimum values for each row. This may be coded into a simple two-liner using PDL to manipulate the matrices.
Example 1:
perl -MPDL -E '
@a=@ARGV; @x=split "", $a[0]; $i=pdl(grep {$x[$_] eq $a[1]} 0..@x-1);
$j=sequence(0+@x); say "@a; $a[1] -> ", ($i->dummy(1)-$j->dummy(0))->abs->minover;
' loveleetcode e
Results:
loveleetcode e; e -> [3 2 1 0 1 0 0 1 2 2 1 0]
Example 2:
perl -MPDL -E '
@a=@ARGV; @x=split "", $a[0]; $i=pdl(grep {$x[$_] eq $a[1]} 0..@x-1);
$j=sequence(0+@x); say "@a; $a[1] -> ", ($i->dummy(1)-$j->dummy(0))->abs->minover;
' aaab b
Results:
aaab b; b -> [3 2 1 0]
The full code is similar:
1 # Perl weekly challenge 248
2 # Task 1: Shortest Distance
3 #
4 # See https://wlmb.github.io/2023/12/17/PWC248/#task-1-shortest-distance
5 use v5.36;
6 use PDL;
7 die <<~"FIN" unless @ARGV==2;
8 Usage: $0 string letter
9 to obtain the shortest distances.
10 FIN
11 my @letters=split "", $ARGV[0];
12 my $i=pdl(grep {$letters[$_] eq $ARGV[1]} 0..@letters-1);
13 my $j=sequence(0+@letters);
14 say "@ARGV; $ARGV[1] -> ", ($i->dummy(1)-$j->dummy(0))->abs->minover;
Examples:
./ch-1.pl loveleetcode e
./ch-1.pl aaab b
Results:
loveleetcode e; e -> [3 2 1 0 1 0 0 1 2 2 1 0]
aaab b; b -> [3 2 1 0]
Task 2: Submatrix Sum
Submitted by: Jorg Sommrey
You are given a NxM matrix A of integers.
Write a script to construct a (N-1)x(M-1) matrix B having elements
that are the sum over the 2x2 submatrices of A,
b[i,k] = a[i,k] + a[i,k+1] + a[i+1,k] + a[i+1,k+1]
Example 1
Input: $a = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]
Output: $b = [
[14, 18, 22],
[30, 34, 38]
]
Example 2
Input: $a = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
Output: $b = [
[2, 1, 0],
[1, 2, 1],
[0, 1, 2]
]
This is also a good example to show off PDL. The result fits a one and a half liner.
Example 1:
perl -MPDL -MPDL::NiceSlice -E '
$m=pdl(shift); $n=pdl($m(0:-2,0:-2),$m(1:-1,0:-2),$m(0:-2,1:-1),$m(1:-1,1:-1))
->mv(-1,0)->sumover; say "$m->$n"
' "[[1,2,3,4],[5,6,7,8],[9,10,11,12]]"
Results:
[
[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
]
->
[
[14 18 22]
[30 34 38]
]
Example 2:
perl -MPDL -MPDL::NiceSlice -E '
$m=pdl(shift); $n=pdl($m(0:-2,0:-2),$m(1:-1,0:-2),$m(0:-2,1:-1),$m(1:-1,1:-1))
->mv(-1,0)->sumover; say "$m->$n"
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]"
Results:
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
->
[
[2 1 0]
[1 2 1]
[0 1 2]
]
The full code is almost identical.
1 # Perl weekly challenge 248
2 # Task 2: Submatrix Sum
3 #
4 # See https://wlmb.github.io/2023/12/17/PWC248/#task-2-submatrix-sum
5 use v5.36;
6 use PDL;
7 use PDL::NiceSlice;
8 die <<~"FIN" unless @ARGV==1;
9 Usage: $0 M
10 to sum 2x2 the overlapped submatrices of matrix M
11 FIN
12 my $m=pdl(shift);
13 my $n=pdl($m(0:-2,0:-2),$m(1:-1,0:-2),$m(0:-2,1:-1),$m(1:-1,1:-1))
14 ->mv(-1,0)->sumover;
15 say "$m->$n"
Examples:
./ch-2.pl "[[1,2,3,4],[5,6,7,8],[9,10,11,12]]"
./ch-2.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]"
Results:
[
[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
]
->
[
[14 18 22]
[30 34 38]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
->
[
[2 1 0]
[1 2 1]
[0 1 2]
]
Alternative
After E. Choroboa pointed out this entry: Fast sliding submatrix sums with PDL (inspired by PWC 248 task 2) I found another solution that may be easily generalized to arbitrary window size. I use conv2d from PDL::Image2D to make a convolution with a kernel of ones, effectively summing submatrices. I discard the edges of the result, where the kernel doesn’t fit. The solutionx fits a two liner.
perl -MPDL -MPDL::Image2D -E '
$m=pdl(shift); $w=shift; $h=shift;say "$m $w -> ", $m->conv2d(ones($w,$h))->
slice([floor(($w-1)/2),floor(-($w+1)/2)], [floor(($h-1)/2),floor(-($h+1)/2)]);
' "[[1,2,3,4],[5,6,7,8],[9,10,11,12]]" 2 2
perl -MPDL -MPDL::Image2D -E '
$m=pdl(shift); $w=shift; $h=shift;say "$m $w -> ", $m->conv2d(ones($w,$h))->
slice([floor(($w-1)/2),floor(-($w+1)/2)], [floor(($h-1)/2),floor(-($h+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 2
perl -MPDL -MPDL::Image2D -E '
$m=pdl(shift); $w=shift; $h=shift;say "$m $w -> ", $m->conv2d(ones($w,$h))->
slice([floor(($w-1)/2),floor(-($w+1)/2)], [floor(($h-1)/2),floor(-($h+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 3
perl -MPDL -MPDL::Image2D -E '
$m=pdl(shift); $w=shift; $h=shift;say "$m $w -> ", $m->conv2d(ones($w,$h))->
slice([floor(($w-1)/2),floor(-($w+1)/2)], [floor(($h-1)/2),floor(-($h+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 2
perl -MPDL -MPDL::Image2D -E '
$m=pdl(shift); $w=shift; $h=shift;say "$m $w -> ", $m->conv2d(ones($w,$h))->
slice([floor(($w-1)/2),floor(-($w+1)/2)], [floor(($h-1)/2),floor(-($h+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 3
Results:
[
[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
]
2 ->
[
[14 18 22]
[30 34 38]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
2 ->
[
[2 1 0]
[1 2 1]
[0 1 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
2 ->
[
[2 2 1]
[1 2 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
3 ->
[
[2 1]
[2 2]
[1 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
3 ->
[
[3 2]
[2 3]
]
Full code:
use v5.36;
use PDL;
use PDL::Image2D;
die <<~"FIN" unless @ARGV==3;
Usage: $0 M W H
where M is a matrix "[[m11,m22...],[m21,m22....],...]"
and W and H are the size of a sliding window to make a matrix of HxW submatrices
and sum the submatrix matrix elements.
FIN
my $m=pdl(shift);
my $w=shift;
my $h=shift;
say "Input: $m Width $w, Height $h -> ",
$m->conv2d(ones($w,$h))->slice([floor(($w-1)/2),floor(-($w+1)/2)],
[floor(($h-1)/2),floor(-($h+1)/2)]);
Examples
./ch-2a.pl "[[1,2,3,4],[5,6,7,8],[9,10,11,12]]" 2 2
./ch-2a.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 2
./ch-2a.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 3
./ch-2a.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 2
./ch-2a.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 3
Results:
Input:
[
[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
]
Width 2, Height 2 ->
[
[14 18 22]
[30 34 38]
]
Input:
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
Width 2, Height 2 ->
[
[2 1 0]
[1 2 1]
[0 1 2]
]
Input:
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
Width 2, Height 3 ->
[
[2 2 1]
[1 2 2]
]
Input:
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
Width 3, Height 2 ->
[
[2 1]
[2 2]
[1 2]
]
Input:
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
Width 3, Height 3 ->
[
[3 2]
[2 3]
]
FFT
For large matrices/kernels it might be preferable to use a Fourier transform to compute the convolution. It seems that the kernel must have a center, thus it must have odd dimensions. For even dimensions, I can zero the last row and/or column. The examples below are small, but serve to show the results are the expected ones. First I show a four-liner and then the full code.
perl -MPDL -MPDL::FFT -E '
$m=pdl(shift); $W=$w=shift; $H=$h=shift; $s=ones($w%2?$w:$w+1, $h%2?$h:$h+1);
$s->slice(-1).=0, ++$w unless $w%2; $s->slice([],-1).=0, ++$h unless $h%2;
$k=kernctr($m, $s); $r=$m->copy; $r->fftconvolve($k); say "$m $W $H -> ",
$r->slice([floor(($W-1)/2),floor(-($W+1)/2)], [floor(($H-1)/2),floor(-($H+1)/2)]);
' "[[1,2,3,4],[5,6,7,8],[9,10,11,12]]" 2 2
perl -MPDL -MPDL::FFT -E '
$m=pdl(shift); $W=$w=shift; $H=$h=shift; $s=ones($w%2?$w:$w+1, $h%2?$h:$h+1);
$s->slice(-1).=0, ++$w unless $w%2; $s->slice([],-1).=0, ++$h unless $h%2;
$k=kernctr($m, $s); $r=$m->copy; $r->fftconvolve($k); say "$m $W $H -> ",
$r->slice([floor(($W-1)/2),floor(-($W+1)/2)], [floor(($H-1)/2),floor(-($H+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 2
perl -MPDL -MPDL::FFT -E '
$m=pdl(shift); $W=$w=shift; $H=$h=shift; $s=ones($w%2?$w:$w+1, $h%2?$h:$h+1);
$s->slice(-1).=0, ++$w unless $w%2; $s->slice([],-1).=0, ++$h unless $h%2;
$k=kernctr($m, $s); $r=$m->copy; $r->fftconvolve($k); say "$m $W $H -> ",
$r->slice([floor(($W-1)/2),floor(-($W+1)/2)], [floor(($H-1)/2),floor(-($H+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 3
perl -MPDL -MPDL::FFT -E '
$m=pdl(shift); $W=$w=shift; $H=$h=shift; $s=ones($w%2?$w:$w+1, $h%2?$h:$h+1);
$s->slice(-1).=0, ++$w unless $w%2; $s->slice([],-1).=0, ++$h unless $h%2;
$k=kernctr($m, $s); $r=$m->copy; $r->fftconvolve($k); say "$m $W $H -> ",
$r->slice([floor(($W-1)/2),floor(-($W+1)/2)], [floor(($H-1)/2),floor(-($H+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 2
perl -MPDL -MPDL::FFT -E '
$m=pdl(shift); $W=$w=shift; $H=$h=shift; $s=ones($w%2?$w:$w+1, $h%2?$h:$h+1);
$s->slice(-1).=0, ++$w unless $w%2; $s->slice([],-1).=0, ++$h unless $h%2;
$k=kernctr($m, $s); $r=$m->copy; $r->fftconvolve($k); say "$m $W $H -> ",
$r->slice([floor(($W-1)/2),floor(-($W+1)/2)], [floor(($H-1)/2),floor(-($H+1)/2)]);
' "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 3
Results:
[
[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
]
2 2 ->
[
[14 18 22]
[30 34 38]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
2 2 ->
[
[2 1 0]
[1 2 1]
[0 1 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
2 3 ->
[
[2 2 1]
[1 2 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
3 2 ->
[
[2 1]
[2 2]
[1 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
3 3 ->
[
[3 2]
[2 3]
]
The corresponding full code is:
use v5.36;
use PDL;
use PDL::FFT;
my $matrix=pdl(shift);
my $width=my $w=shift;
my $height=my $h=shift;
my $small=ones($w%2?$w:$w+1, $h%2?$h:$h+1);
$small->slice(-1).=0, ++$w unless $w%2; # zero row and/or column for even kernels
$small->slice([],-1).=0, ++$h unless $h%2;
my $kernel=kernctr($matrix, $small); #full kernel
my $result=$matrix->copy;
$result->fftconvolve($kernel);
say "$matrix $width $height -> ",
$result->slice([floor(($width-1)/2),floor(-($width+1)/2)],
[floor(($height-1)/2),floor(-($height+1)/2)]);
./ch-2b.pl "[[1,2,3,4],[5,6,7,8],[9,10,11,12]]" 2 2
./ch-2b.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 2
./ch-2b.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 2 3
./ch-2b.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 2
./ch-2b.pl "[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]" 3 3
Results:
[
[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
]
2 2 ->
[
[14 18 22]
[30 34 38]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
2 2 ->
[
[2 1 0]
[1 2 1]
[0 1 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
2 3 ->
[
[2 2 1]
[1 2 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
3 2 ->
[
[2 1]
[2 2]
[1 2]
]
[
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[0 0 0 1]
]
3 3 ->
[
[3 2]
[2 3]
]