Perl Weekly Challenge: Week 164

Challenge 1:

Prime Palindrome

Write a script to find all prime numbers less than 1000, which are also palindromes in base 10. Palindromic numbers are numbers whose digits are the same in reverse. For example, 313 is a palindromic prime, but 337 is not, even though 733 (337 reversed) is also prime.

As is so ofter the case, in Raku we can solve this with a one-liner.

(1 .. 1000).grep({ $_.is-prime && $_ == $_.flip }).join(q{ }).say;

(Full code on Github.)

The code is almost self-explanatory.

The Perl solution is longer because we have to provide our own isPrime() function but the core can also be expressed in one line.

say join q{ }, grep { isPrime($_) && $_ == reverse $_ } 1 .. 1000;

(Full code on Github.)

The Palindromic Primes under 1000 are:

2 3 5 7 11 101 131 151 181 191 313 353 373 383 727 757 787 797 919 929

Challenge 2:

Happy Numbers

Write a script to find the first 8 Happy Numbers in base 10. For more information, please check out Wikipedia.

Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.

Those numbers for which this process end in 1 are happy numbers, while those numbers that do not end in 1 are unhappy numbers.

Example:

19 is Happy Number in base 10, as shown:

19 => 1^2 + 9^2
=> 1   + 81
=> 82 => 8^2 + 2^2
        => 64  + 4
        => 68 => 6^2 + 8^2
            => 36  + 64
            => 100 => 1^2 + 0^2 + 0^2
                    => 1 + 0 + 0
                    => 1

I started my Raku solution by defining an array to hold the results and a counter for integers starting with 1.

    my @happy;
    my $i = 1;

Then, until I have found 8 happy numbers, I check the current number for happiness (which sounds a bit weird when you say it) using the isHappy() function which I will describe next. If it is a happy number, it is added to the @happy array. Then we move on to the next number.

    while @happy.elems < 8 {
        if isHappy($i) {
            @happy.push($i);
        }
        $i++;
    }

And at the end, the happy numbers we found are joined together with spaces and printed out.

    @happy.join(q{ }).say;

The core of the script is in the isHappy() function. This takes an integer to be checked.

sub isHappy(Int $i) {

According to the spec, if the number is not happy (weird!) the intermediate results will eventually loop. I thought hard about how to detect such a cycle but then I noticed a detail in the linked Wikipedia article. It says that for a base-10 happy number, the maximum length of a cycle is 8. So I decided to count the number of tries I take to bring a sum to 1 and if that has not been achieved by 8 tries, consider the number unhappy. (Sorry I just can't get over this.)

    my $tries = 0;

The intermediate result of each try is stored in the variable $s an abbreviation for sum. But sum is the name of a builtin Raku method which I am going to use in this script so I thought $sum might risk confusion.

    my $s = $i;

Then while we have not reached 1...

    while $s != 1 {

If we have tried 8 times, give up and return False. This is not a happy number.

        if $tries == 8 {
            return False;
        }

Split the number into digits, square each digit and sum the results. This will be the next value of $s.

        $s = $s.comb.map({ $_ ** 2 }).sum;

Increment the number of tries.

        $tries++;
    }

If we have reached this point, $s is 1 so this is a happy number and we can return True.

    return True;
}

(Full code on Github.)

In Perl, all we need to add is a sum() function and we can directly translate the Raku version.

sub isHappy {
    my ($i) = @_;
    my $tries = 0;
    my $s = $i;

    while ($s != 1) {
        if ($tries == 8) {
            return undef;
        }
        $s = sum([map { $_ ** 2 } split //, $s]);
        $tries++;
    }

    return 1;
}

my @happy;
my $i = 1;

while (scalar @happy < 10) {
    if (isHappy($i)) {
        push @happy, $i;
    }
    $i++;
}

say join q{ }, @happy;

(Full code on Github.)

The first 8 happy numbers are:

1 7 10 13 19 23 28 31