Perl Weekly Challenge: Week 158
Congratulations and thanks to Mohammed for 3 years of the weekly challenge.
Challenge 1:
Additive Primes
Write a script to find out all
Additive Primes <= 100
.Additive primes are prime numbers for which the sum of their decimal digits are also primes.
Output
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89
This problem can be solved as a one-liner. First we take the range, 2 to 100. (1 is not a prime so we can safely
ignore it.) Then we find the prime numbers in that range by .grep()
ing with .is-prime()
. These prime numbers are filtered
yet again by splitting them into individual digits with .comb()
and summing the digits with [+]
and checking if that number
is prime. The result of all this is a list of additive primes which is joined up with commas via .join()
and printed.
(2 .. 100).grep({ .is-prime }).grep({ ([+] .comb).is-prime }).join(q{, }).say;
Alas we cannot be quite so succint with Perl due to a lack of [+]
and .is-prime
. But as this comes up a lot in these challenges
I have long since developed sum()
and isPrime()
functions and I used them again this week. With these, the core of the solution
can be expressed in one line like this:
say join q{, }, grep { isPrime(sum([split //])) } grep { isPrime($_) } 2 .. 100;
It's just like the Raku solution except the order of operations is right to left instead of left to right.
Challenge 2:
First Series Cuban Primes
Write a script to compute first series Cuban Primes <= 1000
. Please refer wikipedia page for more informations.
Output
7, 19, 37, 61, 127, 271, 331, 397, 547, 631, 919.
Here is the Perl solution:
The Wikepedia page given has a formula for calculating these types of primes in terms of x and y. So first we create a variable to hold the current value of y starting from 1. x is always going to be y + 1 so it can be calculated in terms of $y
. We also
create a list, @cubans
to hold the results.
my $y = 1;
my @cubans;
Then we enter an infinite loop in which to calculate Cuban primes.
while(1) {
A simple transposition of the formula on the Wikipedia page could be:
my $x = $y + 1;
my $p = (($x ** 3) - ($y ** 3)) / ($x - $y);
One might notice that x - y is always 1 so the calculation can be simplified to
my $p - ($x ** 3) - ($y ** 3);
However the page also notes that this formula can be simplified even further to 3y2 + 3y + 1 so I have coded it like this:
my $p = 3 * $y ** 2 + 3 * $y + 1;
This way also has the advantage of removing the need for representing x.
If the value calculated for $p
is greater than 1000, we can stop and break out of the loop.
if ($p > 1000) {
last;
}
If it isn't and $p
is a prime number, we add it to the results.
if (isPrime($p)) {
push @cubans, $p;
}
We increment $y
and do the next iteration of the loop.
$y++;
}
Finally, we join all the results with commas and print them out.
say join q{, }, @cubans;
For Raku, I normally would have followed a similar method as with Perl but I remembered that it has a nifty feature, gather
and take
which I should be using more of. As I understand it, gather
executes a block asynchronously in a coroutine from which results are emitted by take
. So it is more efficient and potentially faster than the traditional approach. Another benefit is that we don't need an intermediate list to store the results; we can directly .join()
and .say()
the output of the gather
block.
my $y = 1;
gather {
loop {
my $p = 3 * $y ** 2 + 3 * $y + 1;
if $p > 1000 {
last;
}
if ($p.is-prime) {
take $p;
}
$y++;
}
}.join(q{, }).say;