Perl Weekly Challenge 125: Pythagorean Triples

These are some answers to the Week 125 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Task 1: Pythagorean Triples

You are given a positive integer $N.

Write a script to print all Pythagorean Triples containing $N as a member. Print -1 if it can’t be a member of any.

Triples with the same set of elements are considered the same, i.e. if your script has already printed (3, 4, 5), (4, 3, 5) should not be printed.

The famous Pythagorean theorem states that in a right angle triangle, the length of the two shorter sides and the length of the longest side are related by a²+b² = c².

A Pythagorean triple refers to the triple of three integers whose lengths can compose a right-angled triangle.

Example:

Input: $N = 5
Output:
    (3, 4, 5)
    (5, 12, 13)

Input: $N = 13
Output:
    (5, 12, 13)
    (13, 84, 85)

Input: $N = 1
Output:
    -1

It has been known since Euclid and is quite easy to prove that any integer larger than 2 can be part of a Pythagorean triple. We’ll use that knowledge in our implementation.

On the other hand, I don’t really know how to be sure that you really produce an exhaustive list of triples for a given input value.

Pythagorean Triples in Raku

There are several possible ways to go for this task, but I decided to build a data structure with all Pythagorean triples within a certain range. This is probably efficient if we’re going to test many input values (as done in the script below), but probably not for one single value.

use v6;

my @squares = map { $_² }, 1..Inf;
my $max = 200;
my $square-set = @squares[0..$max];
my @square-triples = gather {
    for (@squares[0..$max]).combinations(2) -> $comb {
        my $sum = [+] $comb;
        take (|$comb, $sum) if $sum (elem) $square-set;
    }
}
# say @square-triples;
my %look-up = 0 => -1, 1 => -1, 2 => -1;
for @square-triples -> $triple {
    push %look-up, $triple[$_].sqrt => (map { $_.sqrt}, $triple[0..2]) for 0..2;
}
# say %look-up{13};
for 1..20 -> $test {
    say "$test:\t", %look-up{$test};
}

This program displays the following output:

$ raku ./pythagorean-triples.raku
1:      -1
2:      -1
3:      (3 4 5)
4:      (3 4 5)
5:      [(3 4 5) (5 12 13)]
6:      (6 8 10)
7:      (7 24 25)
8:      [(6 8 10) (8 15 17)]
9:      [(9 12 15) (9 40 41)]
10:     [(6 8 10) (10 24 26)]
11:     (11 60 61)
12:     [(5 12 13) (9 12 15) (12 16 20) (12 35 37)]
13:     [(5 12 13) (13 84 85)]
14:     (14 48 50)
15:     [(8 15 17) (9 12 15) (15 20 25) (15 36 39) (15 112 113)]
16:     [(12 16 20) (16 30 34) (16 63 65)]
17:     [(8 15 17) (17 144 145)]
18:     [(18 24 30) (18 80 82)]
19:     (19 180 181)
20:     [(12 16 20) (15 20 25) (20 21 29) (20 48 52) (20 99 101)]

Pythagorean Triples in Raku

Again, we produce a data structure with all Pythagorean triples within a certain range. This is probably efficient if we’re going to test many input values (as done in the script below), but probably not for one single value.

Perl don’t have a built-in combinations function. So, we could use again the recursive combine subroutine of last week’s challenge:

sub combine {
    my $count = shift;
    my @out = @{$_[0]};
    my @in  = @{$_[1]};
    if ($count == 0) {
        push @combinations, [@out];
        return;
    }
    for my $i (0..$#in) {
        combine ($count - 1, [@out, $in[$i]], [@in[0..$i -1], @in[$i+1..$#in]]);
    }
}

and call it thus:

combine 2, [], [2..20]; # populates @combinations

But, here, we only need to produce combinations of two items, and it is therefore simpler to generate them directly like this:

my @combinations;
for my $i (2..200) {
    push @combinations, [$i, $_] for $i+1 .. $max;
}

So, this is my Perl implementation of the task:

use strict;
use warnings;
use feature qw/say/;

my $max = 300;
my @squares = map  $_ * $_ , 1..$max;
my %square_hash = map { $_ => 1 } @squares;
my @combinations;
for my $i (2..200) {
    push @combinations, [$i, $_] for $i+1 .. $max;
}   
my @triples;
for my $comb (@combinations) {
    my $sum_sq = $comb->[0] ** 2 + $comb->[1] ** 2;
    push @triples, [ @$comb, 0 + $sum_sq ** 0.5 ] if exists $square_hash{$sum_sq};
}
my %look_up = (0 => " [ -1 ] ", 1 => " [ -1 ] ", 2 => " [ -1 ] " );
for my $triple (@triples) {
    for my $val (@$triple) {
        $look_up{$val} .= " [ @$triple ] " ;
    }
}
for my $test (1..30) {
    my $result = $look_up{$test};
    say "$test:\t $result";
}

This program displays the following output:

$ perl pythagorean-triples.pl
1:        [ -1 ]
2:        [ -1 ]
3:        [ 3 4 5 ]
4:        [ 3 4 5 ]
5:        [ 3 4 5 ]  [ 5 12 13 ]
6:        [ 6 8 10 ]
7:        [ 7 24 25 ]
8:        [ 6 8 10 ]  [ 8 15 17 ]
9:        [ 9 12 15 ]  [ 9 40 41 ]
10:       [ 6 8 10 ]  [ 10 24 26 ]
11:       [ 11 60 61 ]
12:       [ 5 12 13 ]  [ 9 12 15 ]  [ 12 16 20 ]  [ 12 35 37 ]
13:       [ 5 12 13 ]  [ 13 84 85 ]
14:       [ 14 48 50 ]
15:       [ 8 15 17 ]  [ 9 12 15 ]  [ 15 20 25 ]  [ 15 36 39 ]  [ 15 112 113 ]
16:       [ 12 16 20 ]  [ 16 30 34 ]  [ 16 63 65 ]
17:       [ 8 15 17 ]  [ 17 144 145 ]
18:       [ 18 24 30 ]  [ 18 80 82 ]
19:       [ 19 180 181 ]
20:       [ 12 16 20 ]  [ 15 20 25 ]  [ 20 21 29 ]  [ 20 48 52 ]  [ 20 99 101 ]
21:       [ 20 21 29 ]  [ 21 28 35 ]  [ 21 72 75 ]  [ 21 220 221 ]
22:       [ 22 120 122 ]
23:       [ 23 264 265 ]
24:       [ 7 24 25 ]  [ 10 24 26 ]  [ 18 24 30 ]  [ 24 32 40 ]  [ 24 45 51 ]  [ 24 70 74 ]  [ 24 143 145 ]
25:       [ 7 24 25 ]  [ 15 20 25 ]  [ 25 60 65 ]
26:       [ 10 24 26 ]  [ 26 168 170 ]
27:       [ 27 36 45 ]  [ 27 120 123 ]
28:       [ 21 28 35 ]  [ 28 45 53 ]  [ 28 96 100 ]  [ 28 195 197 ]
29:       [ 20 21 29 ]
30:       [ 16 30 34 ]  [ 18 24 30 ]  [ 30 40 50 ]  [ 30 72 78 ]  [ 30 224 226 ]

I’m very late and have no time this week for the second task.

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on August 22, 2021. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.