Perl Weekly Challenge 245.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 245.
Task 1: Sort Language
Submitted by: Mohammad S Anwar
You are given two array of languages and its popularity.
Write a script to sort the language based on popularity.
Example 1
Input: @lang = ('perl', 'c', 'python')
@popularity = (2, 1, 3)
Output: ('c', 'perl', 'python')
Example 2
Input: @lang = ('c++', 'haskell', 'java')
@popularity = (1, 3, 2)
Output: ('c++', 'java', 'haskell')
The problem may be solved by simply using sort
on the indices of both
arrays. I assume both arrays are input as space separated strings in
@ARGV. This yields a 2-liner.
Example 1:
perl -E '
$l[$_]=[split " ", $ARGV[$_]] for 0,1; @s=map {$l[0][$_]} sort {$l[1][$a]<=>$l[1][$b]}
0..@{$l[0]}-1; say "languages: @{$l[0]}\npopularity: @{$l[1]}\nsorted: @s";
' "perl c python" "2 1 3"
Results:
languages: perl c python
popularity: 2 1 3
sorted: c perl python
Example 2:
perl -E '
$l[$_]=[split " ", $ARGV[$_]] for 0,1; @s=map {$l[0][$_]} sort {$l[1][$a]<=>$l[1][$b]} 0..@{$l[0]}-1;
say "languages: @{$l[0]}\npopularity: @{$l[1]}\nsorted: @s";
' "c++ haskell java" "1 3 2"
Results:
languages: c++ haskell java
popularity: 1 3 2
sorted: c++ java haskell
I can also do a Schwartzian transform for sorting:
perl -MList::MoreUtils=pairwise -E '
$l[$_]=[split " ", $ARGV[$_]]for 0,1;say "languages: @{$l[0]}\npopularity: @{$l[1]}\nsorted: ",
join " ", map {$_->[0]} sort {$a->[1] <=> $b->[1]} pairwise {[$a,$b]} @{$l[0]}, @{$l[1]};
' "perl c python" "2 1 3"
Results:
languages: perl c python
popularity: 2 1 3
sorted: c perl python
However, in this simple case the resulting code is not simpler.
The full code corresponds to the first solution
1 # Perl weekly challenge 245
2 # Task 1: Sort Language
3 #
4 # See https://wlmb.github.io/2023/11/27/PWC245/#task-1-sort-language
5 use v5.36;
6 die <<~"FIN" unless @ARGV && @ARGV%2==0;
7 Usage: $0 L1 P1 [L2 P2...]
8 to sort the space separated list of languages Ln
9 according to their popularity Pn
10 FIN
11 while(@ARGV){
12 my @language=split " ", shift;
13 my @popularity=split " ", shift;
14 warn("Number of elements should coincide: languages: @language vs. popularities @popularity\n"),
15 next unless @language==@popularity;
16 my @sorted=map {$language[$_]} sort {$popularity[$a]<=>$popularity[$b]} 0..@language-1;
17 say "languages: @language\npopularities: @popularity\nsorted: @sorted\n";
18 }
Example:
./ch-1.pl "perl c python" "2 1 3" "c++ haskell java" "1 3 2"
Results:
languages: perl c python
popularities: 2 1 3
sorted: c perl python
languages: c++ haskell java
popularities: 1 3 2
sorted: c++ java haskell
Task 2: Largest of Three
Submitted by: Mohammad S Anwar
You are given an array of integers >= 0.
Write a script to return the largest number formed by concatenating some
of the given integers in any order which is also multiple of 3.
Return -1 if none found.
Example 1
Input: @ints = (8, 1, 9)
Output: 981
981 % 3 == 0
Example 2
Input: @ints = (8, 6, 7, 1, 0)
Output: 8760
Example 3
Input: @ints = (1)
Output: -1
As 10 is congruent to 1 modulo 3, a number is divisible by three if the sum of
its decimal digits is divisible by three. Thus, rearrangement of the
digits does not affect divisibility by three. Call N=NK … N2 N1 N0 the
concatenation of all sorted inputs. Choosing N0 N1<=N1 N0, N1
N2<=N2 N1… then N
would be the largest number that can be obtained by concatenating the
inputs (I am grateful to choroba
for pointing out a mistake in a
pervious solution). Set r=N%3. If r==0 we are done. If r==1, then we can remove from N the smallest
Nk such that Nk%3==1, or the two smallest Nl and Nm such that
Nl%3==Nm%3==2, as 2+2 is congruent to 1 modulo 3. Similarly, if
r==2, then we can remove from N the smallest
Nk such that Nk%3==2, or remove the smallest Nl and Nm such that
Nl%3=Nm%3==1. If necessary, we can choose between removing the single
number Nk or the two numbers Nl and Nm by
comparing Nk and the concatenation of Nl and Nm (assuming l>m) and choosing the
smallest. The resulting number would then be the largest number
that can be formed by concatenating integers from a list and that is divisible by
three. The code seems too complex for a few-liner, so I first jump to the
full code, and then the compact solution:
1 # Perl weekly challenge 245
2 # Task 2: Largest of Three
3 #
4 # See https://wlmb.github.io/2023/11/27/PWC245/#task-2-largest-of-three
5 use v5.36;
6 use List::Util qw(all);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 N1 [N2...]
9 to find the largest concatenation of numbers N_i which yield a
10 multiple of 3.
11 FIN
12 die "Only non-negative numbers allowed" unless all {/\d+/} @ARGV;
13 my $index=0;
14 my $total;
15 my @one;
16 my @two;
17 my @sorted= sort{"$a$b"<=>"$b$a"}@ARGV; # increasing order
18 for(@sorted){
19 my $residue=$_%3;
20 push @one, $index if $residue==1;
21 push @two, $index if $residue==2;
22 $total+=$residue;
23 ++$index;
24 }
25 $total%=3;
26 my @candidates; # if neccesary to remove numbers
27 if($total==1){
28 # remove the smallest number that leaves a residue one
29 push @candidates, [$one[0]] if(@one);
30 # remove the two smallest numbers that leaves a residue two
31 push @candidates, [@two[1,0]] if(@two>=2);
32 }
33 if($total==2){
34 # remove the two smallest numbers that leaves a residue one
35 push @candidates, [@one[1,0]] if(@one>=2);
36 # or the smallest numbers that leaves a residue two
37 push @candidates, [$two[0]] if(@two);
38 }
39 if(@candidates){ # find smallest candidates
40 my @to_remove=map {join "", @sorted[@$_]} @candidates;
41 my $index_to_remove=@candidates == 2 && $to_remove[1]<$to_remove[0]?1:0;
42 splice @sorted, $_, 1 for @{$candidates[$index_to_remove]}; # remove one or two numbers
43 }
44 my $result=join "", reverse @sorted;
45 $result=-1 if $result eq ""; # removed all
46 $result=0 if $result==0; # 0000...=0 is divisible;
47 say "@ARGV -> $result";
Examples:
./ch-2.pl 8 1 9
./ch-2.pl 8 6 7 1 0
./ch-2.pl 1
./ch-2.pl 0 0 0
Results:
8 1 9 -> 981
8 6 7 1 0 -> 8760
1 -> -1
0 0 0 -> 0
8 85 0 -> 8850
Examples by E-Choroba:
./ch-2.pl 4 8 911 # 9114
./ch-2.pl 8 85 0 # 8850
./ch-2.pl 8 89 2 # 8982
./ch-2.pl 8 76 0 # 8760
./ch-2.pl 8 94 0 # 9480
Results:
4 8 911 -> 9114
8 85 0 -> 8850
8 89 2 -> 8982
8 76 0 -> 8760
8 94 0 -> 9480
After a few simplifications, I could compact the code into a 3.5-liner.
Examples:
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 1 9
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 6 7 1 0
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 1
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 85 0
Additional examples:
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 4 8 911 # 9114
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 85 0 # 8850
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 89 2 # 8982
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 76 0 # 8760
perl -E '
$i=0;@s=sort{"$a$b"<=>"$b$a"}@ARGV;for(@s){$t+=($r=$_%3);push @{$r[$r]},$i++;}$t%=3;if($t){
push @c, [$r[$t][0]] if $r[$t];push @c, [@{$r[3-$t]}[1,0]] if $r[3-$t];}if(@c){
@t=map {join "", @s[@$_]} @c;$i=@c==2&&$t[1]<$t[0]?1:0;splice @s, $_, 1 for @{$c[$i]};}
$r=join "", reverse @s;$r=-1 if $r eq "";say "@ARGV -> $r";
' 8 94 0 # 9480
Results:
4 8 911 -> 9114
8 85 0 -> 8850
8 89 2 -> 8982
8 76 0 -> 8760
8 94 0 -> 9480