Perl Weekly Challenge 246: Brute Force Math!
This post presents my solutions to the Perl Weekly Challenge 246.I keep doing the Perl Weekly Challenge in order to mantain my coding skills in good shape, as well as in order to learn new things, with particular regard to Raku, a language that I love.
This week, I solved the following tasks:
- PWC 246 - Task 1 - Raku
- PWC 246 - Task 2 - Raku
- PWC 246 - Task 1 in PostgreSQL PL/Perl
- PWC 246 - Task 2 in PostgreSQL PL/Perl
- PWC 246 - Task 1 in PostgreSQL PL/PgSQL
- PWC 246 - Task 2 in PostgreSQL PL/PgSQL
- PWC 246 - Task 1 in Python
- PWC 246 - Task 2 in Python
Raku Implementations
PWC 246 - Task 1 - Raku Implementation
The first task was about extracting six random numbers (without repetitions) less than49
.
sub MAIN() {
my @lottery;
while ( @lottery.elems < 6 ) {
@lottery.push: $_ if ( ! @lottery.grep( $_ ) ) given ( 49.rand.Int );
}
@lottery.join( "\n" ).say;
}
PWC 246 - Task 2 - Raku Implementation
The second task was about finding out, given an input array of five integers, if the array represents a linear recurrence of second order. Since I’m not good at this kind of math, I implemented it with a brute force approach where I scan from1
to infinity
the possibiliies and exits as soon as I’m sure I’ve found an answer.
sub MAIN( *@nums where { @nums.elems == 5 && @nums.grep( * ~~ Int ).elems == @nums.elems } ) {
for 2 ..^ @nums.elems {
# a[n] = p * a[n-2] + q * a[n-1] with n > 1
my $ok = False;
for 1 .. Inf -> $p {
for 1 .. Inf -> $q {
if ( @nums[ $_ ] == ( $p * @nums[ $_ - 2 ] + $q * @nums[ $_ - 1 ] )
|| @nums[ $_ ] == ( $p * -1 * @nums[ $_ - 2 ] + $q * @nums[ $_ - 1 ] )
|| @nums[ $_ ] == ( $p * -1 * @nums[ $_ - 2 ] + $q * -1 * @nums[ $_ - 1 ] )
|| @nums[ $_ ] == ( $p * @nums[ $_ - 2 ] + $q * -1 * @nums[ $_ - 1 ] ) ) {
$ok = True;
last;
}
}
last if $ok;
}
'False'.say and exit if ! $ok;
}
'True'.say;
}
PL/Perl Implementations
PWC 246 - Task 1 - PL/Perl Implementation
Same approach as in Raku.CREATE OR REPLACE FUNCTION
pwc246.task1_plperl()
RETURNS SETOF int
AS $CODE$
my @lottery;
while ( @lottery < 6 ) {
my $value = int ( rand( 49 ) );
if ( ! grep( { $_ == $value } @lottery ) ) {
push @lottery, $value;
return_next( $value );
}
}
return undef;
$CODE$
LANGUAGE plperl;
PWC 246 - Task 2 - PL/Perl Implementation
Same brute force approach as in Raku. Note that I need to use a C-stylefor
loop to simulate the infinity upper boundary, since inf
from bigint
is not usable in a range.
CREATE OR REPLACE FUNCTION
pwc246.task2_plperl( int[] )
RETURNS bool
AS $CODE$
my ( $nums ) = @_;
die "Need 5 numbers" if ( $nums->@* != 5 );
my $ok = 0;
my $ko = $nums->@* - 2;
for ( 2 .. $nums->@* - 1 ) {
for ( my $p = 1; ; $p++ ) {
for ( my $q = 1; ; $q++ ) {
if ( $nums->@[ $_ ] == ( $p * $nums->[ $_ - 2 ] + $q * $nums->[ $_ - 1 ] )
|| $nums->@[ $_ ] == ( $p * -1 * $nums->[ $_ - 2 ] + $q * $nums->[ $_ - 1 ] )
|| $nums->@[ $_ ] == ( $p * -1 * $nums->[ $_ - 2 ] + $q * -1 * $nums->[ $_ - 1 ] )
|| $nums->@[ $_ ] == ( $p * $nums->[ $_ - 2 ] + $q * -1 * $nums->[ $_ - 1 ] ) ) {
$ok = 1;
$ko--;
last;
}
}
last if $ok;
}
}
return ( $ko == 0 );
$CODE$
LANGUAGE plperl;
PostgreSQL Implementations
PWC 246 - Task 1 - PL/PgSQL Implementation
I use a couple of tricks here, that surely do not make this solution a good one given that I need only six values, but allows me to explain some capabilities of PostgreSQL.CREATE OR REPLACE FUNCTION
pwc246.task1_plpgsql()
RETURNS SETOF INT
AS $CODE$
DECLARE
counting int;
current_value int;
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS lottery( v int, CHECK( v <= 49 ), PRIMARY KEY( v ) );
TRUNCATE lottery;
counting := 0;
WHILE counting < 6 LOOP
current_value := ( random() * 100 )::int;
IF current_value <= 49 THEN
INSERT INTO lottery( v )
VALUES( current_value )
ON CONFLICT ( v ) DO NOTHING;
END IF;
SELECT count(*)
INTO counting
FROM lottery;
END LOOP;
RETURN QUERY
SELECT * FROM lottery;
END
$CODE$
LANGUAGE plpgsql;
I use a temporary table
lottery
to store unique values that I’m going to generate randomly.
The idea is while counting
, that contains the number of rows in the temporary table, is less than 6
, I’m going to generate a new random value by means of random
(multiplied by 100
to ensure it gives me a number between 1
and 100
). If the genrrated value if less than 49
I insert it into the temporary table, skipping the insert in the case the number is already there!
Once the table has been filled with six rows, I can return all of them with a simple query.
PWC 246 - Task 2 - PL/PgSQL Implementation
Same brute force approach shown before. Note that here I simulate a quite infinity upper boud with a99999
fixed value, but it would be possible to use an always increasing loop.
CREATE OR REPLACE FUNCTION
pwc246.task2_plpgsql( nums int[] )
RETURNS bool
AS $CODE$
DECLARE
current_index int;
p int;
q int;
ok bool := false;
ko int;
BEGIN
ko := array_length( nums, 1 ) - 3 + 1;
FOR current_index IN 3 .. array_length( nums, 1 ) LOOP
ok := false;
FOR p IN 1 .. 99999 LOOP
FOR q IN 1 .. 9999 LOOP
IF nums[ current_index ] = ( p * nums[ current_index - 2 ] + q * nums[ current_index - 1 ] )
OR nums[ current_index ] = ( p * -1 * nums[ current_index - 2 ] + q * nums[ current_index - 1 ] )
OR nums[ current_index ] = ( p * -1 * nums[ current_index - 2 ] + q * -1 *nums[ current_index - 1 ] )
OR nums[ current_index ] = ( p * nums[ current_index - 2 ] + q * -1 * nums[ current_index - 1 ] ) THEN
ok := true;
ko := ko - 1;
EXIT;
END IF;
END LOOP;
IF ok THEN
EXIT;
END IF;
END LOOP;
END LOOP;
RETURN ko = 0;
END
$CODE$
LANGUAGE plpgsql;
Python Implementations
PWC 246 - Task 1 - Python Implementation
A very simple implementation, using the random generation and an array to store the values so that I can check if I’ve already got a value before.import sys
import random
# task implementation
def main( argv ):
values = []
while len( values ) < 6:
current_value = random.randint( 1, 49 )
if not current_value in values:
values.append( current_value )
print( "\n".join( map( str, values ) ) )
# invoke the main without the command itself
if __name__ == '__main__':
main( sys.argv[ 1: ] )
PWC 246 - Task 2 - Python Implementation
Another brute force approach. This time I usecount
to simulate the infinity upper bound.
import sys
from itertools import count
# task implementation
def main( argv ):
nums = list( map( int, argv ) )
ko = len( nums ) - 2
for current_index in range( 2, len( nums ) ):
for p in count( start = 1 ):
for q in count( start = 1 ):
if nums[ current_index ] == ( p * nums[ current_index - 2 ] + q * nums[ current_index - 1 ] ) \
or nums[ current_index ] == ( p * -1 * nums[ current_index - 2 ] + q * nums[ current_index - 1 ] ) \
or nums[ current_index ] == ( p * -1 * nums[ current_index - 2 ] + q * -1 * nums[ current_index - 1 ] ) \
or nums[ current_index ] == ( p * nums[ current_index - 2 ] + q * -1 * nums[ current_index - 1 ] ):
ok = True
ko = ko - 1
break
if ok:
break
if ko == 0:
print( 'True' )
else:
print( 'False' )
# invoke the main without the command itself
if __name__ == '__main__':
main( sys.argv[ 1: ] )