From 6243365c0e36082c6052ece795c4983b85135ba4 Mon Sep 17 00:00:00 2001 From: Util Date: Sun, 23 Jul 2023 15:15:56 -0500 Subject: Add TWC 226 solutions by Bruce Gray (Raku only). --- challenge-226/bruce-gray/raku/ch-1.raku | 28 ++++++++++++++ challenge-226/bruce-gray/raku/ch-2.raku | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 challenge-226/bruce-gray/raku/ch-1.raku create mode 100644 challenge-226/bruce-gray/raku/ch-2.raku 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; +} -- cgit