# Sieves and Coins **Challenge 223 solutions in Perl by Matthias Muth** ## Task 1: Count Primes > 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.
This looks very straightforward: get an array of prime numbers and return its length.
The only question is how to get the prime numbers between 2 and *n*. If the number *n* is not too high, the '[Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)' is a fairly simple and easy to implement algorithm to find prime numbers up to a limit *n*.
Its advantage is that it does not need any divisions, and it has a runtime complexity of *O*( *n* log log *n* ), which probably makes it faster than using a prime factorization for each candidate number. Only if `n` gets larger it can run into memory issues. But we are talking about *very* large numbers here, since we are only limited by the RAM available for running our program, and the RAM usage rises linearly with *n* (one integer for each added number checked, which might even be reduced to one *bit* per number).
So let's not worry too much about it, also knowing that the challenge examples are up to *n* = 20 only. The big advantage for us is that it computes and returns the whole set of prime numbers up to *n*. All that is left to do is to count them! So here we go: ```perl use strict; use warnings; use feature 'say'; use feature 'signatures'; no warnings 'experimental::signatures'; use lib '.'; use TestExtractor; use List::Util qw( first ); sub eratosthenes( $n ) { my @non_primes; my $sqrt = sqrt( $n ); my $i = 2; while ( $i <= $sqrt ) { say "trying $i:"; for ( my $j = 2 * $i; $j <= $n; $j += $i ) { say " mark $j as non-prime"; $non_primes[$j] = 1; } $i = first { ! $non_primes[$_] } $i + 1 .. $n; say " next \$i to try: $i"; } say " $i is larger than sqrt( $n ) ($sqrt)"; say "returning ( ", join( " ", grep { ! $non_primes[$_] } 2..$n ), " )"; return grep { ! $non_primes[$_] } 2..$n; } eratosthenes( 20 ); ``` which prints ``` $ ch-1.pl trying 2: mark 4 as non-prime mark 6 as non-prime mark 8 as non-prime mark 10 as non-prime mark 12 as non-prime mark 14 as non-prime mark 16 as non-prime mark 18 as non-prime mark 20 as non-prime next $i to try: 3 trying 3: mark 6 as non-prime mark 9 as non-prime mark 12 as non-prime mark 15 as non-prime mark 18 as non-prime next $i to try: 5 5 is larger than sqrt( 20 ) (4.47213595499958) returning ( 2 3 5 7 11 13 17 19 ) ``` Then the actual solution to the task is this little function: ```perl sub count_primes( $n ) { return scalar eratosthenes( $n ); } ``` ## Task 2: Box Coins > 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.
We need to find out with which box to start. It is the one that delivers the highest sum from taking this coin *and* from finding the highest number possible from taking the rest of the coins in the right order. A typical scenario for a recursive solution! The stop condition is met when there is ony one coin. Then it's obvious which one to take. If there is more than one coin, we use a `map` call to compute the maximum achievable value for all coins, just as described: multiply the coin with its neighbors (if they exist), and do the recursive call for all coins but the current one.
And of course we return the maximum of this list. ```perl sub box_coins { my ( @box ) = @_; return $box[0] if @box == 1; return max( map { ( $box[$_] * ( $_ > 0 ? $box[ $_ - 1 ] : 1 ) * ( $_ < $#box ? $box[ $_ + 1 ] : 1 ) ) + box_coins( @box[ 0 .. $_ - 1, $_ + 1 .. $#box ] ); } 0..$#box ); } ``` #### **Thank you for the challenge!**