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;

(Full code on Github.)

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;

(Full code on Github.)

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;

(Full code on Github.)

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;

(Full code on Github.)