Perl Weekly Challenge 148: eban vs cardano
It is sad that, after more than two years of me doing Raku, I still don’t have any production code project to work on. Therefore, in order to keep my coding and Raku-ing (is that a term?) knowdledge, I try to solve every Perl Weekly Challenge tasks.In the following, the assigned tasks for Challenge 148.
And this week, as for the previous PWC, I had time to quickly implement the tasks also on PostgreSQL
plpgsql
language:
PWC 148 - Task 1
This task was about finding out Eban numbers, those numbers that do not contain the lettere
within their english name.
I was not really inspired by this task, so I brutally created a set of arrays that contains the numbers that do not copntain an e
in their english name, and I grep
for those numbers:
sub MAIN() {
my @eban-units = 2, 4, 6;
my @eban-teens = 12;
my @eban-tens = 3, 4, 5, 6;
$_.say if @eban-units.grep( $_ ) for 1 .. 10;
$_.say if @eban-teens.grep( $_ ) for 11 .. 19;
$_.say if @eban-tens.grep( ( $_ / 10 ).Int ) && @eban-units.grep( $_ % 10 ) for 20 .. 100;
}
PWC 148 - Task 2
This has been a more difficult task, that required me to learn about the=~=
almost equality operator.
The task required to print out the first five Cardano numbers, those that solve a particular equation given three variables.
First of all, I implemented a couple of functions that, given the three variables, provide me the result of the Cardano domain belonging:
multi sub is-cardano-triplet( $a, $b, $c ) {
my $left = .sign * .abs**( 1 / 3 ) given ( $a + $b * $c.sqrt );
my $right = .sign * .abs**( 1 / 3 ) given ( $a - $b * $c.sqrt );
return 1 =~= ( $left + $right );
}
multi sub is-cardano-triplet( @triplet ) {
return is-cardano-triplet( @triplet[ 0 ], @triplet[ 1 ], @triplet[ 2 ] );
}
The second implementation of the
is-cardano-triplet
function is only a placeholder to simplify when I use an array, and this will become clearer later. However, I could have simply used the |@triplet
flatting operator.
The
$left
and $right
are computed as the parts of the equation. The .sign
method provides +1
or -1
depending on the sign of the result (the part in the given
) and then I computer the third root of the absolute value, thus providing the resulting signed value. I needed a little help here about the math.
However, I was expecting this resulted in an integer result, but instead the result was a
Rat
, thus requiring me to use the new (to me) =~=
operator, that is almost equal operator (see the documentation).
With all in place, I used a nested loop to generate a triplet, and then I compared every single permutation of the triple to see if the result was good enough:
sub MAIN( Int $limit = 5 ) {
my @triplets = lazy gather {
for 1 .. Inf -> $a {
for 1 ..^ $a -> $b {
for 1 ..^ $b -> $c {
$_.take if is-cardano-triplet( $_ ) for ( $a, $b, $c ).permutations;
}
}
}
};
@triplets[ 0 .. $limit ].join( "\n" ).say;
}
I used a
lazy gather
to stop the computation as soon as the end is reached, and in fact I print out only the first $limit
entries.
This took near 7 seconds on my machine.
PWC 148 - Task 1 in PostgreSQL
The implementation, bit to bit, of the Raku solution for this task:SELECT v
FROM generate_series( 1, 10 ) v
WHERE
v IN ( 2, 4, 6 )
UNION
SELECT v
FROM generate_series( 11, 19 ) v
WHERE v IN ( 12 )
UNION
SELECT v
FROM generate_series( 20, 100 ) v
WHERE
v % 10 IN ( 2, 4, 6 )
AND ( v / 10 )::int IN ( 3, 4, 5, 6 );
A single query can do the trick.
PWC 148 - Task 2 in PostgreSQL
A recursive CTE implementation of the Raku solution for this task, limiting the numbers to30
to avoid too much joins.
Note that I search for the triplets that provide a sum that is near 1
by a small value.
WITH RECURSIVE
triplets AS
(
SELECT a::numeric, b::numeric, c::numeric
FROM generate_series( 1, 30 ) a
, generate_series( 1, 30 ) b
, generate_series( 1, 30 ) c
ORDER BY a, b, c
)
, cardano_sum AS
(
SELECT a, b, c,
( a + b * sqrt( c ) ) AS l
,( a - b * sqrt( c ) ) AS r
FROM triplets
)
, cardano AS
(
SELECT a, b, c, l, r
, CASE WHEN l < 0 THEN -1 ELSE 1 END * pow( abs( l )::numeric, 1/3::numeric )
+ CASE WHEN r < 0 THEN -1 ELSE 1 END * pow( abs( r )::numeric, 1/3::numeric )
AS triplet_sum
FROM cardano_sum
)
SELECT *
FROM cardano
WHERE
abs( 1 - triplet_sum::numeric ) <= 0.0000000001
LIMIT 5
;
It takes 54 seconds to complete, and is a lot more of the Raku implementation. Why? Because the above query performs the computation for every join available.