Perl Weekly Challenge 223.

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

Task 1: Count Primes

Submitted by: Mohammad S Anwar
You are given a positive integer, $n.

Write a script to find the total count of primes less than or equal to the given integer.


Example 1
Input: $n = 10
Output: 4

Since there are 4 primes (2,3,5,7) less than or equal to 10.
Example 2
Input: $n = 1
Output: 0
Example 3
Input: $n = 20
Output: 8

Since there are 4 primes (2,3,5,7,11,13,17,19) less than or equal to 20.

I go for the canned solution using Math::Prime::Util which has the function prime_count. This yields a very simple oneliner:

perl -MMath::Prime::Util=prime_count -E '
say "There are ", prime_count($_), " primes below $_" for @ARGV
' 10 20 100000

Results:

There are 4 primes below 10
There are 8 primes below 20
There are 9592 primes below 100000

The full code follows:

 1  # Perl weekly challenge 223
 2  # Task 1:  Count Primes
 3  #
 4  # See https://wlmb.github.io/2023/06/27/PWC223/#task-1-count-primes
 5  use v5.36;
 6  use Math::Prime::Util qw(prime_count);
 7  die <<~"FIN" unless @ARGV;
 8      Usage: $0 N1 [N2...]
 9      to find out how many primes there are upto N1, N2...
10      FIN
11  say "There are ", prime_count($_), " primes below $_" for @ARGV;

Example:

./ch-1.pl 10 20 100000

Results:

There are 4 primes below 10
There are 8 primes below 20
There are 9592 primes below 100000

Task 2: Box Coins

Submitted by: Mohammad S Anwar
You are given an array representing box coins, @box.

Write a script to collect the maximum coins until you took out all boxes.
If we pick box[i] then we collect the coins $box[i-1] * $box[i] * $box[i+1].
If $box[i+1] or $box[i-1] is out of bound then treat it as 1 coin.


Example 1:
Input: @box = (3, 1, 5, 8)
Output: 167

Step 1: pick box [i=1] and collected coins 3 * 1 * 5 => 15.  Boxes available (3, 5, 8).
Step 2: pick box [i=1] and collected coins 3 * 5 * 8 => 120. Boxes available (3, 8).
Step 3: pick box [i=0] and collected coins 1 * 3 * 8 => 24.  Boxes available (8).
Step 4: pick box [i=0] and collected coins 1 * 8 * 1 => 8.   No more box available.
Example 2:
Input: @box = (1, 5)
Output: 10

Step 1: pick box [i=0] and collected coins 1 * 1 * 5 => 5. Boxes available (5).
Step 2: pick box [i=0] and collected coins 1 * 5 * 1 => 5. No more box available.

I assume all boxes have positive integers. My first guess was to pick the box with the smallest number at each step, so that upon deleting it, boxes with larger numbers come together and multiply themselves. Nevertheless, the boxes at the extremes are bounded by virtual boxes with only one coin which cannot be deleted, so I guess it is best not to choose the boxes at the extremes (as any factor would not be worse than 1), unless there is no alternative. This yields a three liner, which I explain in the full code below.

Exapmle 1:

perl -MList::UtilsBy=min_by -E '
@x=(1,@ARGV,1); $t+=$c while(defined ($c=n())); say "@ARGV -> $t";
sub n(){return if @x==2; return splice @x,1,1 if @x==3; $i=min_by {$x[$_]} @x==4?(1,2):(2..@x-3);
$v=splice @x,$i,1; return $v*$x[$i-1]*$x[$i]}
' 3 1 5 8

Results:

3 1 5 8 -> 167

Exapmle 2:

perl -MList::UtilsBy=min_by -E '
@x=(1,@ARGV,1); $t+=$c while(defined ($c=n())); say "@ARGV -> $t";
sub n(){return if @x==2; return splice @x,1,1 if @x==3; $i=min_by {$x[$_]} @x==4?(1,2):(2..@x-3);
$v=splice @x,$i,1; return $v*$x[$i-1]*$x[$i]}
' 1 5

Results:

1 5 -> 10

The full code follows:

 1  # Perl weekly challenge 223
 2  # Task 2:  Box Coins
 3  #
 4  # See https://wlmb.github.io/2023/06/27/PWC223/#task-2-box-coins
 5  use v5.36;
 6  use List::UtilsBy qw(min_by);
 7  die <<~"FIN" unless @ARGV;
 8      Usage: $0 N1 [N2...]
 9      to collect the maximum amount of coins from boxes with N1 N2... coins
10      FIN
11  my @extended=(1,@ARGV,1); # input args with guards on either side
12  my $total;
13  my $amount;
14  $total += $amount while(defined ($amount=next_amount())); # pick coins while they last
15  say "@ARGV -> $total";
16
17  sub next_amount(){ # choose a box and compute coins
18      return if @extended==0+2; # no more boxes, the 2 comes from the boundary guards
19      return splice @extended,1,1 if @extended==3; # only one box
20      my $i=min_by {$extended[$_]} # choose smallest box
21          @extended==2+2
22  	?(1,2)                   # only two remaining boxes, consider boundaries
23  	:(2..@extended-3);       # more boxes, disregard boundaries
24      my $value=splice @extended,$i,1; # remove box from array
25      return $value*$extended[$i-1]*$extended[$i]; # compute value from neighbor boxes
26  }

Example:

./ch-2.pl 3 1 5 8
./ch-2.pl 1 5

Results:

3 1 5 8 -> 167
1 5 -> 10

To allow numbers below 1, I would have to add code to consider the extremes if they are smaller than 1, and to ignore them if they are larger.

Written on June 27, 2023