Perl Weekly Challenge 206.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 206.

Task 1: Shortest Time

Submitted by: Mohammad S Anwar
You are given a list of time points, at least 2, in the 24-hour clock format HH:MM.

Write a script to find out the shortest time in minutes between any two time points.

Example 1
Input: @time = ("00:00", "23:55", "20:00")
Output: 5

Since the difference between "00:00" and "23:55" is the shortest (5 minutes).
Example 2
Input: @array = ("01:01", "00:50", "00:57")
Output: 4
Example 3
Input: @array = ("10:10", "09:30", "09:00", "09:55")
Output: 15

Assuming the input is well formatted, the task is easily solved by converting times to minutes, ordering them and subtracting consecutive times. The only subtlety is that the earliest time for a given day may be closer to the latest time of the previous day than to the next earliest of the same day; time goes in circles. This is solved by adding the earliest time (plus 24 hours) at the end of the sorted list. It is not even necessary to verify there are two inputs. The shortest time between a time point and its daily repetitions is 24 hours. All this fits a two-liner.

perl -MList::Util=min -E '
($m,@r)=sort {$a<=>$b} map{($h,$m)=split(":"); $h*60+$m} @ARGV; push @r, $m+24*60;
say join " ", @ARGV, "->", min map{($d,$m)=($_-$m, $_); $d} @r
' 00:00 23:55 20:00

Results:

00:00 23:55 20:00 -> 5

Other examples:

perl -MList::Util=min -E '
($m,@r)=sort {$a<=>$b} map{($h,$m)=split(":"); $h*60+$m} @ARGV; push @r, $m+24*60;
say join " ", @ARGV, "->", min map{($d,$m)=($_-$m, $_); $d} @r
' 01:01 00:50 00:57
perl -MList::Util=min -E '
($m,@r)=sort {$a<=>$b} map{($h,$m)=split(":"); $h*60+$m} @ARGV; push @r, $m+24*60;
say join " ", @ARGV, "->", min map{($d,$m)=($_-$m, $_); $d} @r
' 10:10 09:30 09:00 09:55

Results:

01:01 00:50 00:57 -> 4
10:10 09:30 09:00 09:55 -> 15

The full code is similar, but makes some checks on the input

 1  # Perl weekly challenge 206
 2  # Task 1:  Shortest Time
 3  #
 4  # See https://wlmb.github.io/2023/02/27/PWC206/#task-1-shortest-time
 5  use v5.36;
 6  use List::Util qw(min);
 7  die <<~"FIN" unless @ARGV;
 8      Usage: $0 T1 [T2...]HH:MM_2...
 9      to find the shortest time between the time points T1, T2...,
10      each in the 24h format HH:MM
11      FIN
12  my ($current, @rest)=sort {$a <=> $b} map {to_minutes($_)} @ARGV;
13  push @rest, $current + 24*60; # Add first time as last time, next day
14  my $min = min map {my $diff=$_-$current; $current=$_; $diff} @rest;
15  say join " ", @ARGV, "->", $min;
16
17  sub to_minutes($s){
18      die "Wrong format, expected HH:MM: $s\n" unless $s=~/(\d\d?):(\d\d?)/;
19      my ($hour, $min)=($1, $2);
20      die "Hour should obey 0<=hour<24: $s" unless $hour<24;
21      die "Minute should obey 0<=minute<60: $s" unless $min<60;
22      return $hour*60+$min;
23  }

Examples:

./ch-1.pl 00:00 23:55 20:00
./ch-1.pl 01:01 00:50 00:57
./ch-1.pl 10:10 09:30 09:00 09:55

Results:

00:00 23:55 20:00 -> 5
01:01 00:50 00:57 -> 4
10:10 09:30 09:00 09:55 -> 15

Task 2: Array Pairings

Submitted by: Mohammad S Anwar
You are given an array of integers having even number of elements..

Write a script to find the maximum sum of the minimum of each pairs.

Example 1
Input: @array = (1,2,3,4)
Output: 4

Possible Pairings are as below:
a. (1,2) and (3,4). So min(1,2) + min(3,4) => 1 + 3 => 4
b. (1,3) and (2,4). So min(1,3) + min(2,4) => 1 + 2 => 3
c. (1,4) and (2,3). So min(1,4) + min(2,3) => 2 + 1 => 3

So the maxium sum is 4.
Example 2
Input: @array = (0,2,1,3)
Output: 2

Possible Pairings are as below:
a. (0,2) and (1,3). So min(0,2) + min(1,3) => 0 + 1 => 1
b. (0,1) and (2,3). So min(0,1) + min(2,3) => 0 + 2 => 2
c. (0,3) and (2,1). So min(0,3) + min(2,1) => 0 + 1 => 1

So the maximum sum is 2.

I get the maximum sum if I construct my pairs starting with a pair that has the largest element of all 2N. Then the largest possible minimum would be the largest of the remaining elements 2N-1. I repeat using the remaining 2(N-1) elements until I exhaust the array and sum all the minima to get the result. Equivalently, I can sort all elements choose every second one and sum them. This fits a one-liner:

perl -MList::Util=sum -E 'say join " ", @ARGV, "->", sum map {$i++%2?():$_} (sort {$a<=>$b} @ARGV)' 1 2 3 4
perl -MList::Util=sum -E 'say join " ", @ARGV, "->", sum map {$i++%2?():$_} (sort {$a<=>$b} @ARGV)' 0 2 1 3

Results:

1 2 3 4 -> 4
0 2 1 3 -> 2

The full code is similar, with an added check on the number of arguments.

 1  # Perl weekly challenge 206
 2  # Task 2:  Array Pairings
 3  #
 4  # See https://wlmb.github.io/2023/02/27/PWC206/#task-2-array-pairings
 5  use v5.36;
 6  use List::Util qw(sum);
 7  die <<~"FIN" unless @ARGV && @ARGV%2==0;
 8      Usage: $0 N1 N2 [N3 N4...]
 9      to find the maximum of the sum of the minima of each pair (Ni, Nj)
10      taken over all possible pairings. The number of arguments should be even.
11      FIN
12  my $i = 0; # counter
13  my $max = sum          # sum
14      map {$i++%2?():$_} # every second element
15      sort {$a<=>$b}     # of the sorted (ascending)
16      @ARGV;             # input
17  say join " ", @ARGV, "->", $max;

Examples:

./ch-2.pl 1 2 3 4
./ch-2.pl 0 2 1 3

Results:

1 2 3 4 -> 4
0 2 1 3 -> 2
Written on February 27, 2023