This is my response to The Weekly Challenge #393.
Input: $n = 20
Output: 12
(3,4,5), (4,3,5), (5,12,13),(6,8,10),
(8,6,10), (8,15,17), (9,12,15),(12,5,13),
(12,9,15),(12,16,20),(15,8,17),(16,12,20)
Example 2:
Input: $n = 7
Output: 2
(3,4,5),(4,3,5)
Example 3:
Input: $n = 1
Output: 0
Example 4:
Input: $n = 15
Output: 8
Example 5:
Input: $n = 30
Output: 22
Euclid's algorithm (see e.g. en.wikipedia.org/wiki/Pythagorean_triple > "Generating a triple"), from around year 300 BC, gives us a neat way of solving this.
I have used $p instead of Wikipedia's n, as that one is
used for the input. Other than that, this is just a formula to code job.
#! /usr/bin/env raku
unit sub MAIN (UInt $n, :v(:$verbose));
my $count = 0;
for 2 .. $n.sqrt.Int -> $m
{
for 1 ..^ $m -> $p
{
next unless gcd($m, $p) == 1;
next unless ($m - $p) % 2;
my $c = $m**2 + $p**2;
next unless $c <= $n;
my $a = $m**2 - $p**2;
my $b = 2 * $m * $p;
my $k = $n div $c;
if $verbose
{
for 1 .. $k -> $i
{
say ": ({$a * $i}, {$b * $i}, {$c * $i}) \
& ({$b * $i}, {$a * $i}, {$c * $i}) [i:$i]";
}
}
$count += 2 * $k;
}
}
say $count;
sub gcd(Int $a is copy, Int $b is copy)
{
while $b
{
($a, $b) = ($b, $a % $b);
}
$a;
}
Running it:
$ ./pythagoras-multiplied 20
12
$ ./pythagoras-multiplied 7
2
$ ./pythagoras-multiplied 1
0
$ ./pythagoras-multiplied 15
8
$ ./pythagoras-multiplied 30
22
Looking good.
With verbose mode:
$ ./pythagoras-multiplied -v 20
: (3, 4, 5) & (4, 3, 5) [i:1]
: (6, 8, 10) & (8, 6, 10) [i:2]
: (9, 12, 15) & (12, 9, 15) [i:3]
: (12, 16, 20) & (16, 12, 20) [i:4]
: (5, 12, 13) & (12, 5, 13) [i:1]
: (15, 8, 17) & (8, 15, 17) [i:1]
12
$ ./pythagoras-multiplied -v 7
: (3, 4, 5) & (4, 3, 5) [i:1]
2
$ ./pythagoras-multiplied -v 1
0
$ ./pythagoras-multiplied -v 15
: (3, 4, 5) & (4, 3, 5) [i:1]
: (6, 8, 10) & (8, 6, 10) [i:2]
: (9, 12, 15) & (12, 9, 15) [i:3]
: (5, 12, 13) & (12, 5, 13) [i:1]
8
$ ./pythagoras-multiplied -v 30
: (3, 4, 5) & (4, 3, 5) [i:1]
: (6, 8, 10) & (8, 6, 10) [i:2]
: (9, 12, 15) & (12, 9, 15) [i:3]
: (12, 16, 20) & (16, 12, 20) [i:4]
: (15, 20, 25) & (20, 15, 25) [i:5]
: (18, 24, 30) & (24, 18, 30) [i:6]
: (5, 12, 13) & (12, 5, 13) [i:1]
: (10, 24, 26) & (24, 10, 26) [i:2]
: (15, 8, 17) & (8, 15, 17) [i:1]
: (7, 24, 25) & (24, 7, 25) [i:1]
: (21, 20, 29) & (20, 21, 29) [i:1]
22
Input: $str = "hello"
Output: 9
The ordinal values of "hello" are [104,101,108,108,111], summing up to
532. The nearest prime number to 532 is 523, resulting in an absolute difference
of 9.
Example 2:
Input: $str = "football"
Output: 2
Starting with the values [102,111,111,116,98,97,108,108] and the sum 841.
We find 839 as the nearest prime number, so the difference is 2.
Example 3:
Input: $str = "a"
Output: 0
Example 4:
Input: $str = "challenge"
Output: 2
The ordinal values of "challenge" are [99, 104, 97, 108, 108, 101, 110,
103, 101], which sum up to 931. The nearest prime number to 931 is 929, so the
difference is 2.
Example 5:
Input: $str = "perl"
Output: 2
The ordinal values of "perl" are [112, 101, 114, 108], summing up to 435.
Nearest prime is 433, so the difference is 2.
#! /usr/bin/env raku
unit sub MAIN (Str $str where $str ~~ /^ <[a..zA..Z]>+ $/,
:v(:$verbose));
my @ords = $str.ords;
my $sum = @ords.sum;
my $diff = 0;
my $prime;
loop
{
if is-prime($sum - $diff)
{
$prime = $sum - $diff;
last;
}
if is-prime($sum + $diff)
{
$prime = $sum + $diff;
last;
}
$diff++;
}
if $verbose
{
say ": Ordinals: " ~ @ords.join(", ");
say ": Sum: $sum";
say ": Nearest prime: $prime";
}
say $diff;
[3] Ensure English letters only.
[6]
Get a list of ordinal values for each character in the string with
ords, the plural version of the one-character-at-a-time ord.
See docs.raku.org/routine/ords for more information about ords.
See docs.raku.org/routine/ord for more information about ord.
[7] Get the sum of those values.
[8] The difference from this sum towards the nearest prime number. We start at zero, to include the sum itself in the primeness check.
[9] The actual prime number we found.
[11] An eternal loop, with exit strategies in [16] and [21].
[13] Do we have a prime before the sum?
[15] If so, take note of the prime.
[16] and exit the loop.
[18] Do we have a prime after the sum?
[20] As [15].
[21] As [16].
[23] Increase the distance, ready for the next loop iteration.
[33] Print the difference (to the nearest prime).
Running it:
$ ./prime-step hello
9
$ ./prime-step football
2
$ ./prime-step a
0
$ ./prime-step challenge
2
$ ./prime-step perl
2
Looking good.
With verbose mode:
$ ./prime-step -v hello
: Ordinals: 104, 101, 108, 108, 111
: Sum: 532
: Nearest prime: 523
9
$ ./prime-step -v football
: Ordinals: 102, 111, 111, 116, 98, 97, 108, 108
: Sum: 851
: Nearest prime: 853
2
$ ./prime-step -v a
: Ordinals: 97
: Sum: 97
: Nearest prime: 97
0
$ ./prime-step -v challenge
: Ordinals: 99, 104, 97, 108, 108, 101, 110, 103, 101
: Sum: 931
: Nearest prime: 929
2
$ ./prime-step -v perl
: Ordinals: 112, 101, 114, 108
: Sum: 435
: Nearest prime: 433
2
And that's it.