Peter
Peter Campbell Smith

Truncatable primes
and find the pentagon

Weekly challenge 147 — 10 January 2022

Week 147 - 10 Jan 2022

Task 2

Task — Pentagon numbers

Write a script to find the first pair of Pentagon Numbers whose sum and difference are also a Pentagon Number. Pentagon numbers are defined as P(n) = n(3n - 1)/2.

Examples


Example 1:
The first 10 Pentagon Numbers are:
1, 5, 12, 22, 35, 51, 70, 92, 117 and 145.
P(4) + P(7) = 22 + 70 = 92 = P(8)
but
P(4) - P(7) = |22 - 70| = 48 is not a Pentagon Number.

Analysis

My first attempt at this worked, but was rather slow. I generated successive PNs, checked the difference between each new one and all the preceding ones, thus creating a queue of pairs which met the difference criterion.

I then continued generating PNs, and when I got to one that matched one of the queued sums, I had a result.

My second attempt - the one I have submitted - was faster. I used the fact, gleaned from Wikipedia, that any PN gives an integer result in this formula:

(sqrt(24 * PN + 1) + 1) / 6

Using this avoids the need to identify any PNs beyond the larger member of the pair.

I did worry about applying the Wikipedia formula, because determining that a floating point operation results in an integer result is a little tricky. For example, in some contexts, this:

$x = 1/3;
$y = $x + $x + $x;
say int($y);

results in 0 rather than the expected 1. This is, roughly, because 0.333 + 0.333 + 0.333 = 0.999 which truncates to 0. Happily Perl, on my system at least, does not make that error.

Script


#!/usr/bin/perl

# Peter Campbell Smith - 2022-01-10
# PWC 147 task 2

use v5.28;
use warnings;
use strict;

find_the_pair();

sub find_the_pair {
    
    my ($n, $i, $s, $m, $diff, $sum, %p, @f, $start);
    
    for ($n = 1; ; $n ++) {
        
        # find pentagon numbers sequentially 
        next unless ($i = is_pentagonal($n));   # so $n is the $i'th pentagon

        # so $n is pentagonal
        $f[$i] = $n;        # and the $i'th pentagon number is $f[$i]
        $p{$n} = $i;        # if n is a pentagon, it is the $p{$n}'th one
        next if $n == 1;
        
        # check the difference and sum of this pentagon number ($n) and all smaller ones ($m)
        for $m (1 .. $i - 1)    {
            $diff = $n - $f[$m]; 
            $sum = $n + $f[$m];
            
            # difference is not a pentagon number
            next unless $p{$diff};
            next unless $s = is_pentagonal($sum);   # sum is not a pentagon number
            
            # result!
            say qq[\nPentagon no $i is $n];
            say qq[Pentagon no $m is $f[$m]];
            say qq[Their sum is $sum which is pentagon number $s];          
            say qq[Their difference is $diff which is pentagon number $p{$diff}];
            return;
        }
    }
}

sub is_pentagonal {
    
    # per Wikipedia 
    my $test = (sqrt(24 * $_[0] + 1) + 1) / 6;
    my $test1 = $test - int($test + 1e-19);
    return (abs($test1) <= 1e-19) ? $test : 0;
}

Output


Pentagon no 2167 is 7042750
Pentagon no 1020 is 1560090
Their sum is 8602840 which is pentagon number 2395
Their difference is 5482660 which is 
   pentagon number 1912