diff options
| author | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2020-09-13 02:33:07 -0700 |
|---|---|---|
| committer | PerlMonk Athanasius <PerlMonk.Athanasius@gmail.com> | 2020-09-13 02:33:07 -0700 |
| commit | b5749d01705c4c3e99ddaa1a0e87d07da1d4b4e1 (patch) | |
| tree | 602ec8c87b0d9b0ffd14c281bf78345ada5f4a8f /challenge-077/athanasius | |
| parent | 94243bd5e0988d280b2ebd4afc0dbc3ad125c956 (diff) | |
| download | perlweeklychallenge-club-b5749d01705c4c3e99ddaa1a0e87d07da1d4b4e1.tar.gz perlweeklychallenge-club-b5749d01705c4c3e99ddaa1a0e87d07da1d4b4e1.tar.bz2 perlweeklychallenge-club-b5749d01705c4c3e99ddaa1a0e87d07da1d4b4e1.zip | |
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #077
On branch branch-for-challenge-077
Changes to be committed:
new file: challenge-077/athanasius/perl/ch-1.pl
new file: challenge-077/athanasius/perl/ch-2.pl
new file: challenge-077/athanasius/raku/ch-1.raku
new file: challenge-077/athanasius/raku/ch-2.raku
Diffstat (limited to 'challenge-077/athanasius')
| -rw-r--r-- | challenge-077/athanasius/perl/ch-1.pl | 252 | ||||
| -rw-r--r-- | challenge-077/athanasius/perl/ch-2.pl | 167 | ||||
| -rw-r--r-- | challenge-077/athanasius/raku/ch-1.raku | 215 | ||||
| -rw-r--r-- | challenge-077/athanasius/raku/ch-2.raku | 174 |
4 files changed, 808 insertions, 0 deletions
diff --git a/challenge-077/athanasius/perl/ch-1.pl b/challenge-077/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..a2d7ea0398 --- /dev/null +++ b/challenge-077/athanasius/perl/ch-1.pl @@ -0,0 +1,252 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 077 +========================= + +Task #1 +------- +*Fibonacci Sum* + +Submitted by: Mohammad S Anwar + +You are given a positive integer $N. + +UPDATE: 2020-09-07 09:00:00 +Write a script to find out all possible combination of Fibonacci Numbers +required to get $N on addition. + +You are *NOT* allowed to repeat a number. Print 0 if none found. + +Example 1: + + Input: $N = 6 + + Output: + 1 + 2 + 3 = 6 + 1 + 5 = 6 + +Example 2: + + Input: $N = 9 + + Output: + 1 + 8 = 9 + 1 + 3 + 5 = 9 + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Notes +----- + +The Fibonacci numbers are: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... +[1],[2], but for this Task they are taken to be: 1, 2, 3, 5, 8, 13, 21, ... +--i.e., 0 is excluded, and 1 is included only once. So the Task may be restated +as follows: find all distinct subsets of the set { 1, 2, 3, 4, 8, 13, 21, ... } +such that the sum of their members equals $N. + +Note also that "positive integer" is taken to mean "Natural number", i.e., an +integer > 0, so the case of $N = 0 is excluded. + +I have not found any value for $N which produces an empty solution set. + +Memoization: For small values of $N, the calculation is already fast and memoi- +zation has little effect. But for larger values, the speedup is significant. +For example, with $N = 98,765,432 (2,218 combinations): + + ------------------------------------------------------------ + Rec. calls Time (s) Cache (bytes) + ------------------------------------------------------------ + Without memoization: 453,275 22.12 -- + With memoization: 30 0.15 13,890,979 + ------------------------------------------------------------ + +or with $N = 987,654,321 (4,997 combinations): + + ------------------------------------------------------------ + Rec. calls Time (s) Cache (bytes) + ------------------------------------------------------------ + Without memoization: 6,692,527 275.00 -- + With memoization: 35 0.23 30,212,067 + ------------------------------------------------------------ + +[1] https://oeis.org/A000045 +[2] https://en.wikipedia.org/wiki/Fibonacci_number + +=cut +#============================================================================== + + # Exports: +use strict; +use warnings; +use Const::Fast; # const() +use Getopt::Long; # GetOptions() +use List::MoreUtils qw( firstidx lastidx none ); +use Memoize; # memoize() +use Regexp::Common qw( number ); # %RE{num} + +#------------------------------------------------------------------------------ +# Constant +#------------------------------------------------------------------------------ + +const my $USAGE => +"Usage: + perl $0 [--hide-sums] <N> + + <N> The target sum (a positive integer) + --hide-sums Suppress display of all the distinct combinations?\n"; + +#------------------------------------------------------------------------------ +# File-local variables +#------------------------------------------------------------------------------ + +my @f_Fibonacci; # Fibonacci numbers: 1, 2, 3, 5, 8, 13, ... +my %f_Cum_sums; # Fibonacci numbers (keys) and their cumulative sums (vals) + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 077, Task #1: Fibonacci Sum (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my ($N, $hide_sums) = parse_command_line(); + + init_fib_nums($N); + + memoize('find_fib_sums'); + + my @sums = find_fib_sums($N); + + display_results($N, \@sums, $hide_sums); +} + +#------------------------------------------------------------------------------ +sub find_fib_sums # Recursive subroutine +#------------------------------------------------------------------------------ +{ + my ($N) = @_; + my @terms; + + if ($N == 0) # Base case (a): 0 --> empty sum + { + @terms = ( [] ); + } + elsif ($N <= 2) # Base cases (b) & (c): 1 --> 1, 2 --> 2 + { + @terms = ( [$N] ); + } + else # Recursive cases + { + my $max = lastidx { $_ <= $N } @f_Fibonacci; + my $min = firstidx { $f_Cum_sums{$_} >= $N } @f_Fibonacci; + + for my $f (reverse @f_Fibonacci[$min .. $max]) + { + my @prev_terms = find_fib_sums($N - $f); + my @new_terms; + + for my $t (@prev_terms) + { + if (none { $_ == $f } @$t) + { + # N.B. Can't push to @$t, because $t (an array reference) + # is already entered in the memoization cache; pushing to + # @$t corrupts the cache data + + push @new_terms, [@$t, $f]; + } + # else: $f is already present in @$t, so omit $t from @new_terms + } + + push @terms, @new_terms; + } + } + + return @terms; +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $hide_sums = 0; + + GetOptions + ( + 'hide-sums' => \$hide_sums, + + ) or die "ERROR in command line arguments\n$USAGE"; + + my $args = scalar @ARGV; + $args == 1 or die "ERROR: Found $args command-line arguments, " . + "expected 1\n$USAGE"; + + my $N = $ARGV[0]; + $N =~ /\A$RE{num}{int}\z/ + or die "ERROR: \$N ($N) must be an integer\n$USAGE"; + $N > 0 or die "ERROR: \$N ($N) must be > 0\n$USAGE"; + + return ($N, $hide_sums); +} + +#------------------------------------------------------------------------------ +sub init_fib_nums +#------------------------------------------------------------------------------ +{ + @f_Fibonacci = (1, 2); + %f_Cum_sums = (1 => 1, 2 => 3); + + my ($N) = @_; + my $fib = 2; + my $sum = 3; + + until ($fib >= $N) + { + $fib = $f_Fibonacci[-2] + $f_Fibonacci[-1]; + + push @f_Fibonacci, $fib; + + $sum += $fib; + + $f_Cum_sums{ $fib } = $sum; + } +} + +#------------------------------------------------------------------------------ +sub display_results +#------------------------------------------------------------------------------ +{ + my ($N, $sums, $hide_sums) = @_; + my $count = scalar @$sums; + + printf "Input: \$N = %d\n\n" . + "Output: %d %scombination%s of Fibonacci numbers sum%s to %d%s\n", + $N, $count, $count == 1 ? '' : 'different ', + $count == 1 ? '' : 's', + $count == 1 ? 's' : '', + $N, $count == 0 || $hide_sums ? '' : ':'; + + unless ($hide_sums) + { + printf " %s = %d\n", join(' + ', sort { $a <=> $b } @$_), $N + for @$sums; + } +} + +############################################################################### diff --git a/challenge-077/athanasius/perl/ch-2.pl b/challenge-077/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..05b00b1302 --- /dev/null +++ b/challenge-077/athanasius/perl/ch-2.pl @@ -0,0 +1,167 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 077 +========================= + +Task #2 +------- +*Lonely X* + +Submitted by: Mohammad S Anwar + +You are given m x n character matrix consists of O and X only. + +Write a script to count the total number of X surrounded by O only. Print 0 if +none found. + +Example 1: + + Input: [ O O X ] + [ X O O ] + [ X O O ] + + Output: 1 as there is only one X at the first row last column surrounded by + only O. + +Example 2: + + Input: [ O O X O ] + [ X O O O ] + [ X O O X ] + [ O X O O ] + + Output: 2 + + a) First X found at Row 1 Col 3. + + b) Second X found at Row 3 Col 4. + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +use strict; +use warnings; +use Const::Fast; + +const my $USAGE => +"Usage: + perl $0 [<rows> ...] + + [<rows> ...] 1+ same-width rows, each a string of 'O' and 'X' chars\n"; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 077, Task #2: Lonely X (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my $matrix = get_matrix(@ARGV); + + print_matrix($matrix); + + my $lonelyX = find_lonely_X($matrix); + my $count = scalar @$lonelyX; + + print " Output: $count\n", $count ? "\n" : ''; + + my $i = 0; + printf " %d. Lonely X found at Row %d Col %d\n", ++$i, @$_ for @$lonelyX; +} + +#------------------------------------------------------------------------------ +sub find_lonely_X +#------------------------------------------------------------------------------ +{ + my ($matrix) = @_; + + my @lonely_X; + my $max_row = $#$matrix; + my $max_col = $#{ $matrix->[0] }; + + for my $row (0 .. $max_row) + { + COLUMN: for my $col (0 .. $max_col) + { + if ($matrix->[$row][$col] eq 'X') + { + for my $row_delta (-1, 0, 1) + { + my $neighbr_row = $row + $row_delta; + next if $neighbr_row < 0 || + $neighbr_row > $max_row; + + for my $col_delta (-1, 0, 1) + { + next if $row_delta == 0 && + $col_delta == 0; # Self, not neighbour! + + my $neighbr_col = $col + $col_delta; + next if $neighbr_col < 0 || + $neighbr_col > $max_col; + + next COLUMN # This X is not lonely + if $matrix->[$neighbr_row][$neighbr_col] eq 'X'; + } + } + + push @lonely_X, [$row + 1, $col + 1]; # A lonely X + } + } + } + + return \@lonely_X; +} + +#------------------------------------------------------------------------------ +sub get_matrix +#------------------------------------------------------------------------------ +{ + my @rows = @_; + + scalar @rows > 0 or die $USAGE; + + my $width = length $rows[0]; + my @matrix; + + for my $i (0 .. $#rows) + { + my $row = $rows[$i]; + + $row =~ / ( [^OX] ) /x + and die "Invalid character '$1' in the input matrix\n$USAGE"; + + length $row == $width + or die 'Inconsistent number of columns in row ' . ($i + 1) . + "\n$USAGE"; + + $matrix[$i] = [ split '', $row ]; + } + + return \@matrix; +} + +#------------------------------------------------------------------------------ +sub print_matrix +#------------------------------------------------------------------------------ +{ + my ($matrix) = @_; + + printf " Input: [ %s ]\n", join ' ', @{ $matrix->[0 ] }; + printf " [ %s ]\n", join ' ', @{ $matrix->[$_] } for 1 .. $#$matrix; + print "\n"; +} + +############################################################################### diff --git a/challenge-077/athanasius/raku/ch-1.raku b/challenge-077/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..9cbb281b3d --- /dev/null +++ b/challenge-077/athanasius/raku/ch-1.raku @@ -0,0 +1,215 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 077 +========================= + +Task #1 +------- +*Fibonacci Sum* + +Submitted by: Mohammad S Anwar + +You are given a positive integer $N. + +UPDATE: 2020-09-07 09:00:00 +Write a script to find out all possible combination of Fibonacci Numbers +required to get $N on addition. + +You are *NOT* allowed to repeat a number. Print 0 if none found. + +Example 1: + + Input: $N = 6 + + Output: + 1 + 2 + 3 = 6 + 1 + 5 = 6 + +Example 2: + + Input: $N = 9 + + Output: + 1 + 8 = 9 + 1 + 3 + 5 = 9 + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Notes +----- + +The Fibonacci numbers are: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... +[1],[2], but for this Task they are taken to be: 1, 2, 3, 5, 8, 13, 21, ... +--i.e., 0 is excluded, and 1 is included only once. So the Task may be restated +as follows: find all distinct subsets of the set { 1, 2, 3, 4, 8, 13, 21, ... } +such that the sum of their members equals $N. + +Note also that "positive integer" is taken to mean "Natural number", i.e., an +integer > 0, so the case of $N = 0 is excluded. + +I have not found any value for $N which produces an empty solution set. + +Memoization: For small values of $N, the calculation is already fast and memo- +ization has little effect. But for larger values, the speedup is significant. +For example, with $N = 9,876,543: + + -------------------------------------------- + Rec. calls Time (s) + -------------------------------------------- + Without memoization: 24,451 19.61 + With memoization: 23 1.73 + -------------------------------------------- + +or with $N = 98,765,432: + + -------------------------------------------- + Rec. calls Time (s) + -------------------------------------------- + Without memoization: 453,275 545.24 + With memoization: 30 8.60 + -------------------------------------------- + +[1] https://oeis.org/A000045 +[2] https://en.wikipedia.org/wiki/Fibonacci_number + +=end comment +#============================================================================== + +use List::MoreUtils < firstidx lastidx >; +use Memoize; + +#------------------------------------------------------------------------------ +# File-local variables +#------------------------------------------------------------------------------ + +my UInt @f_Fibonacci; # Fibonacci nums: 1, 2, 3, 5, 8, 13, ... +my UInt %f_Cum-sums; # Fibonacci nums (keys) & their cumulative sums (vals) + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 077, Task #1: Fibonacci Sum (Raku)\n".put; +} + +##============================================================================= +sub MAIN +( + UInt:D $N where { $N > 0 }, #= The target sum (a positive integer) + Bool:D :$hide-sums = False, #= Suppress display of all the distinct + #= combinations? +) +##============================================================================= +{ + init-fib-nums($N); + + memoize(&find-fib-sums); + + my Array[UInt] @sums = find-fib-sums($N); + + display-results($N, @sums, $hide-sums); +} + +#------------------------------------------------------------------------------ +sub find-fib-sums # Recursive subroutine +( + UInt:D $N # Note: 0 must be allowed here! +--> Array:D[Array:D[UInt:D]] +) +#------------------------------------------------------------------------------ +{ + my Array[UInt] @sums; + + if $N == 0 # Base case (a): 0 --> empty sum + { + @sums.push: Array[UInt].new(); + } + elsif $N <= 2 # Base cases (b) & (c): 1 --> 1, 2 --> 2 + { + @sums.push: Array[UInt].new($N); + } + else # Recursive cases + { + my UInt $max = lastidx { $_ <= $N }, @f_Fibonacci; + my UInt $min = firstidx { %f_Cum-sums{$_} >= $N }, @f_Fibonacci; + + for @f_Fibonacci[$min .. $max].reverse -> UInt $f + { + my Array[UInt] @prev-terms = find-fib-sums($N - $f); + my Array[UInt] @new-terms; + + for @prev-terms -> Array[UInt] $term + { + @new-terms.push: Array[UInt].new(|$term, $f) if $f ∉ $term; + } + + @sums.append: @new-terms; + } + } + + return @sums; +} + +#------------------------------------------------------------------------------ +sub init-fib-nums( UInt:D $N ) +#------------------------------------------------------------------------------ +{ + @f_Fibonacci = 1, 2; + %f_Cum-sums = 1 => 1, 2 => 3; + + my UInt $fib = 2; + my UInt $sum = 3; + + until $fib >= $N + { + $fib = @f_Fibonacci[*-2] + @f_Fibonacci[*-1]; + + @f_Fibonacci.push: $fib; + + $sum += $fib; + + %f_Cum-sums{ $fib } = $sum; + } +} + +#------------------------------------------------------------------------------ +sub display-results( UInt:D $N, Array:D[UInt:D] @sums, Bool:D $hide-sums ) +#------------------------------------------------------------------------------ +{ + my UInt $count = @sums.elems; + + ("Input: \$N = %d\n\n" ~ + "Output: %d %scombination%s of Fibonacci numbers sum%s to %d%s\n").printf: + $N, $count, $count == 1 ?? '' !! 'different ', + $count == 1 ?? '' !! 's', + $count == 1 ?? 's' !! '', + $N, $count == 0 || $hide-sums ?? '' !! ':'; + + unless $hide-sums + { + " %s = %d\n".printf: .sort.join(' + '), $N for @sums; + } +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + $usage.put; +} + +############################################################################### diff --git a/challenge-077/athanasius/raku/ch-2.raku b/challenge-077/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..ddfd0ab646 --- /dev/null +++ b/challenge-077/athanasius/raku/ch-2.raku @@ -0,0 +1,174 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 077 +========================= + +Task #2 +------- +*Lonely X* + +Submitted by: Mohammad S Anwar + +You are given m x n character matrix consists of O and X only. + +Write a script to count the total number of X surrounded by O only. Print 0 if +none found. + +Example 1: + + Input: [ O O X ] + [ X O O ] + [ X O O ] + + Output: 1 as there is only one X at the first row last column surrounded by + only O. + +Example 2: + + Input: [ O O X O ] + [ X O O O ] + [ X O O X ] + [ O X O O ] + + Output: 2 + + a) First X found at Row 1 Col 3. + + b) Second X found at Row 3 Col 4. + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2020 PerlMonk Athanasius # +#--------------------------------------# + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 077, Task #2: Lonely X (Raku)\n".put; +} + +##============================================================================= +sub MAIN +( + *@rows where { @rows .elems > 0 && #= 1+ same-width rows, each a + @rows[0].chars > 0 } #= string of 'O' and 'X' chars +) +##============================================================================= +{ + my Str @str-rows = @rows; + my Array[Str] @matrix = get-matrix(@str-rows); + print-matrix( @matrix ); + + my Pair @lonely-X = find-lonely-X(@matrix); + my UInt $count = @lonely-X.elems; + + " Output: %d\n%s".printf: $count, $count ?? "\n" !! ''; + + my UInt $i = 0; + " %d. Lonely X found at Row %d Col %d\n".printf: ++$i, .kv for @lonely-X; +} + +#------------------------------------------------------------------------------ +sub find-lonely-X( Array:D[Str:D] @matrix --> Array:D[Pair:D] ) +#------------------------------------------------------------------------------ +{ + my Pair @lonely-X; + my UInt $max-row = @matrix.end; + my UInt $max-col = @matrix[0].elems - 1; + + for 0 .. $max-row -> UInt $row + { + COLUMN: for 0 .. $max-col -> UInt $col + { + if @matrix[$row; $col] eq 'X' + { + for -1, 0, 1 -> Int $row-delta + { + my Int $neighbour-row = $row + $row-delta; + next if $neighbour-row < 0 || + $neighbour-row > $max-row; + + for -1, 0, 1 -> Int $col-delta + { + next if $row-delta == 0 && + $col-delta == 0; # Self, not neighbour! + + my Int $neighbour-col = $col + $col-delta; + next if $neighbour-col < 0 || + $neighbour-col > $max-col; + + next COLUMN # This X is not lonely + if @matrix[$neighbour-row; $neighbour-col] eq 'X'; + } + } + + push @lonely-X, Pair.new($row + 1, $col + 1); # A lonely X + } + } + } + + return @lonely-X; +} + +#------------------------------------------------------------------------------ +sub get-matrix( Str:D @rows --> Array:D[Str:D] ) +#------------------------------------------------------------------------------ +{ + my UInt $width = @rows[0].chars; + my Array[Str] @matrix[ @rows.elems ]; + + for 0 .. @rows.end -> UInt $i + { + my Str $row = @rows[$i]; + + $row ~~ / ( <-[ O X ]> ) / + and error("Invalid character '$0' in the input matrix"); + + $row.chars == $width + or error("Inconsistent number of columns in row { $i + 1 }"); + + my Str @chars = $row.split: '', :skip-empty; + @matrix[$i] = @chars; + } + + return @matrix; +} + +#------------------------------------------------------------------------------ +sub print-matrix( Array:D[Str:D] @matrix ) +#------------------------------------------------------------------------------ +{ + " Input: [ %s ]\n".printf: @matrix[0 ].join: ' '; + " [ %s ]\n".printf: @matrix[$_].join: ' ' for 1 .. @matrix.end; + + ''.put; +} + +#------------------------------------------------------------------------------ +sub error( Str:D $message ) +#------------------------------------------------------------------------------ +{ + "ERROR: $message".put; + + USAGE(); + + exit; +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + $usage.put; +} + +############################################################################### |
