aboutsummaryrefslogtreecommitdiff
path: root/challenge-211
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2023-04-10 00:36:29 +0100
committerGitHub <noreply@github.com>2023-04-10 00:36:29 +0100
commit57355f5064046cebfd68a5934fe0d8ec3d881e0b (patch)
tree36a8dd79af81c1976a69473b7638642ca989c7b8 /challenge-211
parentbee358b75061fd8a86d9d59668dcc1138fab6b23 (diff)
parent10f1ed83fc5222e38b57e74b5c404c8af03af7c3 (diff)
downloadperlweeklychallenge-club-57355f5064046cebfd68a5934fe0d8ec3d881e0b.tar.gz
perlweeklychallenge-club-57355f5064046cebfd68a5934fe0d8ec3d881e0b.tar.bz2
perlweeklychallenge-club-57355f5064046cebfd68a5934fe0d8ec3d881e0b.zip
Merge pull request #7876 from Util/c211
Add TWC 211 solutions by Bruce Gray (Raku only).
Diffstat (limited to 'challenge-211')
-rw-r--r--challenge-211/bruce-gray/raku/ch-1.raku120
-rw-r--r--challenge-211/bruce-gray/raku/ch-2.raku58
2 files changed, 178 insertions, 0 deletions
diff --git a/challenge-211/bruce-gray/raku/ch-1.raku b/challenge-211/bruce-gray/raku/ch-1.raku
new file mode 100644
index 0000000000..754810b1a6
--- /dev/null
+++ b/challenge-211/bruce-gray/raku/ch-1.raku
@@ -0,0 +1,120 @@
+# Three solutions.
+
+# My "official" answer to the task.
+# For any two adjacent rows, all elements (except the last ) of the first row
+# must match all elements (except the first) of the next row.
+# With a small refactor to use an iterator and @last&@this rows,
+# this would work efficiently with an enormous number of rows.
+sub task1 ( @m --> Bool ) {
+ for @m.rotor( 2 => -1 ) -> ( $top, $bottom ) {
+ return False unless $top.head(*-1) eqv $bottom.skip(1);
+ }
+ return True;
+}
+
+# All credit for this idea goes to Mark Anderson:
+# @m[ ^@m.end; ^@m[0].end ] eqv @m[ 1..@m.end; 1..@m[1].end ]
+# https://github.com/manwar/perlweeklychallenge-club/blob/master/challenge-211/mark-anderson/raku/ch-1.raku#L24
+# A Toeplitz matrix minus its right and bottom edges will be identical
+# to that matrix minus its left and top edges.
+# Mark's range-and-semicolon wording is a perfect expression of that idea, if you already know that idea.
+# I wrote my version differently, attempting to better convey the idea to those who *don't* already know it.
+# Then, I ended up writing all this text anyway, so my success is doubtful.
+sub task1_concise ( @m ) {
+ return @m.head(*-1).map({ .head(*-1) }) # Omitting bottom and right edges.
+ eqv @m.tail(*-1).map({ .tail(*-1) }); # Omitting top and left edges.
+}
+
+
+# This is the first thing I wrote when I read the task.
+# Walking each element of the first row,
+# it checks that every value matches along the diagonal,
+# returning False on a mis-match.all the diagonal values checks each diagonal.
+# Since that process misses all the diagonal starting points (besides [0,0])
+# on the left edge of the matrix, the second loop handles those.
+sub task1_first_attempt ( @m --> Bool ) {
+ for @m[0].keys -> $col {
+ my $first_of_top_edge = @m[0][$col];
+ for 1 .. min(@m.end, @m[0].end - $col) -> $row {
+ return False unless @m[$row][$col+$row] eqv $first_of_top_edge;
+ }
+ }
+ for @m.keys.skip -> $row {
+ my $first_of_left_edge = @m[$row][0];
+ for 1 .. min(@m[0].end, @m.end-$row) -> $col {
+ return False unless @m[$row+$col][$col] eqv $first_of_left_edge;
+ }
+ }
+ return True;
+}
+
+
+my @tests =
+ (
+ (
+ < 4 3 2 1 >,
+ < 5 4 3 2 >,
+ < 6 5 4 3 >,
+ ),
+ True,
+ ),
+ (
+ (
+ < 1 2 3 >,
+ < 3 2 1 >,
+ ),
+ False,
+ ),
+
+ # Example 1, but final element differs
+ (
+ (
+ < 4 3 2 1 >,
+ < 5 4 3 2 >,
+ < 6 5 4 9 >,
+ ),
+ False,
+ ),
+
+ (
+ (
+ < A B C D E F G H I J K >,
+ < Z A B C D E F G H I J >,
+ < Y Z A B C D E F G H I >,
+ < X Y Z A B C D E F G H >,
+ < W X Y Z A B C D E F G >,
+ < V W X Y Z A B C D E F >,
+ < U V W X Y Z A B C D E >,
+ ),
+ True,
+ ),
+ (
+ ( # Just like last test, but `V` on last line now `_`
+ < A B C D E F G H I J K >,
+ < Z A B C D E F G H I J >,
+ < Y Z A B C D E F G H I >,
+ < X Y Z A B C D E F G H >,
+ < W X Y Z A B C D E F G >,
+ < V W X Y Z A B C D E F >,
+ < U _ W X Y Z A B C D E >,
+ ),
+ False,
+ ),
+ (
+ (
+ < A B C >,
+ < Z A B >,
+ < Y Z A >,
+ < X Y Z >,
+ < W X Y >,
+ < V W X >,
+ < U V W >,
+ ),
+ True,
+ ),
+;
+use Test;
+plan +@tests;
+for @tests -> ( $in, $expected ) {
+ is task1($in), $expected;
+}
diff --git a/challenge-211/bruce-gray/raku/ch-2.raku b/challenge-211/bruce-gray/raku/ch-2.raku
new file mode 100644
index 0000000000..c30553b231
--- /dev/null
+++ b/challenge-211/bruce-gray/raku/ch-2.raku
@@ -0,0 +1,58 @@
+# The average of a solution group will == the average of the whole array.
+# The average of a solution group is .sum/.elems,
+# and .elems is fixed during each loop of $group_size,
+# so we can pre-calculate the sum to search for.
+# A further optimization comes from the task specifying that all elements are integers.
+# Their sum must be an integer, so if the "target sum" is not an integer, we can skip the whole group of that size.
+# e.g. 1..8 has an average of 9/2; a group of size 3 can never average to 9/2, because the divisor will be 3.
+sub task2 ( @ns where { @ns.all ~~ Int } --> Bool ) {
+ my Rat $average = @ns.sum / @ns.elems;
+
+ for 1 .. (@ns.elems div 2) -> $group_size {
+
+ my Rat $target_sum = $average * $group_size;
+
+ next unless $target_sum.denominator == 1;
+
+ return True if @ns.combinations($group_size)ยป.sum.any == $target_sum;
+ }
+
+ return False;
+}
+
+# This was my first solution.
+# Averages are Rats, so `==` need not worry about precision.
+# No need to create the second group, since its .sum and its .elems
+# can be calculated as the difference from the first group.
+sub task2_original ( @ns --> Bool ) {
+ my $all_sum = @ns.sum;
+ my $all_elems = @ns.elems;
+
+ for 1 .. (@ns.elems div 2) -> $group_size {
+ for @ns.combinations($group_size) {
+ my $sum = .sum;
+ my $elems = .elems;
+
+ my $average_A = $sum / $elems;
+ my $average_B = ($all_sum - $sum) / ($all_elems - $elems);
+ return True if $average_A == $average_B;
+ }
+ }
+
+ return False;
+}
+
+
+my @tests =
+ ( ( 1, 2, 3, 4, 5, 6, 7, 8 ), True ),
+ ( ( 1, 3 ), False ),
+
+ ( ( 1, 3, 1, 3 ), True ),
+ ( ( 8, 7, 9 ), True ),
+ ( ( 8, 7, 8 ), False ),
+;
+use Test;
+plan +@tests;
+for @tests -> ( $in, $expected ) {
+ is task2($in), $expected;
+}