aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--challenge-226/bruce-gray/raku/ch-1.raku28
-rw-r--r--challenge-226/bruce-gray/raku/ch-2.raku65
2 files changed, 93 insertions, 0 deletions
diff --git a/challenge-226/bruce-gray/raku/ch-1.raku b/challenge-226/bruce-gray/raku/ch-1.raku
new file mode 100644
index 0000000000..93e9ca6066
--- /dev/null
+++ b/challenge-226/bruce-gray/raku/ch-1.raku
@@ -0,0 +1,28 @@
+sub task1 ( Str $s, Int @is --> Str ) {
+ die "Bad @is: @is[]" unless @is.Bag eqv (^$s.chars).Bag;
+
+ # Essentially a super-efficient Radix sort:
+ my @r;
+ @r[ @is ] = $s.comb;
+ return @r.join;
+
+ # Alternatively, single expression:
+ # return (@is Z $s.comb).sort».[1].join;
+
+ # My first attempt at a solution,
+ # affected by having to leave out Radix
+ # from my TPRC talk on Sorting:
+ # my @c = $s.comb;
+ # return @c[ @c.keys.sort({ @is[$_] }) ].join;
+}
+
+
+my @tests =
+ ( 'challenge' , 'lacelengh' , (3,2,0,5,4,8,6,7,1) ),
+ ( 'perlraku' , 'rulepark' , (4,7,3,1,0,5,2,6) ),
+;
+use Test;
+plan +@tests;
+for @tests -> ( $expected, $in_str, @in_dexes ) {
+ is task1($in_str, Array[UInt].new(@in_dexes)), $expected;
+}
diff --git a/challenge-226/bruce-gray/raku/ch-2.raku b/challenge-226/bruce-gray/raku/ch-2.raku
new file mode 100644
index 0000000000..4aa82eae2c
--- /dev/null
+++ b/challenge-226/bruce-gray/raku/ch-2.raku
@@ -0,0 +1,65 @@
+sub task2 ( Int @ns --> UInt ) {
+ my @n = @ns.grep(* != 0).sort.squish;
+ # Below this point, @n stays in sorted order,
+ # so removing later zeros and finding .min
+ # can always be done efficiently on the .head,
+ # instead of scanning the whole array.
+ #
+ # Further, because we removed duplicates,
+ # there will always be exactly one `zero`
+ # after the hypered-subtraction.
+ # So, we can write `shift @n;`
+ # instead of `shift @n while @n and @n.head == 0;`
+
+ my $c = 0;
+ while @n {
+ @n »-=» @n.head; # Removes minimum from every array element.
+ shift @n;
+ $c++;
+ }
+ return $c;
+}
+
+# sub task2_first_attempt_had_no_optimization ( Int @ns --> UInt ) {
+# my @n = @ns.grep: * != 0;
+# my $c = 0;
+# while @n {
+# @n »-=» @n.min;
+# @n .= grep: * != 0;
+# $c++;
+# }
+# return $c;
+# }
+
+{
+# The task allows for subtracting a quantity smaller than the minimum.
+# e.g. 3,5,8,13 ; we could choose to subtract 1 or 2 instead of 3.
+# However, in every case, the slot that already held the minimum still does,
+# so the next step(s) must just use up the min more slowly.
+# So, I am satisfied that the `always use .min in its entirety` must always yield the best result.
+# If I am correct in that logic, then the looping version could be replaced
+# with `@ns.grep(* != 0).unique.elems`, gaining efficiency while killing any clear linkage to the original task.
+ sub task2_alt ( Int @ns --> UInt ) {
+ return @ns.grep(* != 0).unique.elems;
+ }
+ for ^100 {
+ my Int @in = (0..100).roll(200);
+ my $t1 = task2(@in);
+ my $t2 = task2_alt(@in);
+
+ die "$t1 $t2 @in[]" if $t1 != $t2;
+ }
+}
+
+
+
+my @tests =
+ ( 3, (1, 5, 0, 3, 5) ),
+ ( 0, (0,) ),
+ ( 4, (2, 1, 4, 0, 3) ),
+;
+use Test;
+plan +@tests;
+for @tests -> ( $expected, @in ) {
+ is task2( Array[UInt].new(@in) ), $expected;
+}