This is my response to The Weekly Challenge #371.
Input: @seq = qw(a c ? g i)
Output: e
The pattern of the sequence is +2,+2,+2,+2.
1: a
3: c
5: e
7: g
9: i
Example 2:
Input: @seq = qw(a d ? j m)
Output: g
The pattern of the sequence is +3,+3,+3,+3.
1: a
4: d
7: g
10: j
13: m
Example 3:
Input: @seq = qw(a e ? m q)
Output: i
The pattern of the sequence is +4,+4,+4,+4.
1: a
5: e
9: i
13: m
17: q
Example 4:
Input: @seq = qw(a c f ? k)
Output: h
The pattern of the sequence is +2,+3,+2,+3.
1: a
3: c
6: f
8: h
11: k
Example 5:
Input: @seq = qw(b e g ? l)
Output: j
The pattern of the sequence is +3,+2,+3,+2.
2: b
5: e
7: g
10: j
12: l
If we call the five numbers a, b, c, d and e - and the pattern
x, y, x, y - we get the following:
b = a + x
c = b + y
d = c + x
e = d + y
x = b - a or x = d - c
y = c - b or y = e - d
Each missing value can be computed like this, when we replace x and y with two
of the five numbers:
a = b - d + c
b = a + d - c
c = b + e - d
d = c + b - a
e = d + c - b
#! /usr/bin/env raku
unit sub MAIN (*@seq where @seq.elems == 5 # [1]
&& @seq.join ~~ /^<[a..z]>*\?<[a..z]>*$/, # [1a]
:v(:$verbose));
my @trans = @seq.map({ $_ eq '?' ?? '?' !! $_.ord - 'a'.ord + 1 }); # [2]
my ($a, $b, $c, $d, $e) = @trans; # [3]
my $index = @seq.first('?', :k); # [4]
my $x; # [5]
my $y; # [6]
my $letter; # [7]
given $index # [8]
{
when 0 { $letter = $b - $d + $c; $x = $d - $c; $y = $e - $d; } # [9a]
when 1 { $letter = $a + $d - $c; $x = $d - $c; $y = $e - $d; } # [9b]
when 2 { $letter = $b + $e - $d; $x = $b - $a; $y = $e - $d; } # [9c]
when 3 { $letter = $c + $b - $a; $x = $b - $a; $y = $c - $b; } # [9d]
when 4 { $letter = $d + $c - $b; $x = $b - $a; $y = $c - $b; } # [9e]
}
if $verbose
{
say ": Translated: { @trans.join(", ") }";
say ": Question mark at index: $index";
say ": Pattern: $x, $y, $x, $y";
say ": New letter: $letter";
}
say chr('a'.ord + $letter -1); # [10]
[1] A slurpy array with 5 elements, which joined together must satisfy the regex; zero or more lowercase letters, a question mark, and zero or more lowercase letters [1a].
[2] We compute this array with the numeric translation of the letters, as given in the challenge text. The question mark is kept as is.
[3] Assign the numeric values to these named variables, as they are easier to work with.
[4] . Get the first instance (the only, in our case) of a question mark with
. We have to use the :k (key) adverb to get the index instead of the
value.
[5] The x value will end up here.
[6] Ditto for the y value.
[7] The numeric value of the missing letter will end up here.
[8] Inspect the value with given.
See docs.raku.org/syntax/default when for more information about given/when/default.
[9] For each missing value (the when part), compute it.
[10] Convert the numeric value back to a letter, and print it.
Running it:
$ ./missing-letter a c '?' g i
e
$ ./missing-letter a d '?' j m
g
$ ./missing-letter a e '?' m q
i
$ ./missing-letter a c f '?' k
h
$ ./missing-letter b e g '?' l
j
Looking good.
With verbose mode:
$ ./missing-letter -v a c '?' g i
: Translated: 1, 3, 0, 7, 9
: Question mark at index: 2
: Pattern: 2, 2, 2, 2
: New letter: 5
e
$ ./missing-letter -v a d '?' j m
: Translated: 1, 4, ?, 10, 13
: Question mark at index: 2
: Pattern: 3, 3, 3, 3
: New letter: 7
g
$ ./missing-letter -v a e '?' m q
: Translated: 1, 5, ?, 13, 17
: Question mark at index: 2
: Pattern: 4, 4, 4, 4
: New letter: 9
i
$ ./missing-letter -v a c f '?' k
: Translated: 1, 3, 6, ?, 11
: Question mark at index: 3
: Pattern: 2, 3, 2, 3
: New letter: 8
h
$ ./missing-letter -v b e g '?' l
: Translated: 2, 5, 7, ?, 12
: Question mark at index: 3
: Pattern: 3, 2, 3, 2
: New letter: 10
j
Let us add some more:
$ ./missing-letter -v '?' b c d e
: Translated: 0, 2, 3, 4, 5
: Question mark at index: 0
: Pattern: 1, 1, 1, 1
: New letter: 1
a
$ ./missing-letter -v a '?' c d e
: Translated: 1, ?, 3, 4, 5
: Question mark at index: 1
: Pattern: 1, 1, 1, 1
: New letter: 2
b
$ ./missing-letter -v a b c d '?'
: Translated: 1, 2, 3, 4, ?
: Question mark at index: 4
: Pattern: 1, 1, 1, 1
: New letter: 5
e
$ ./missing-letter -v e d '?' c b
: Translated: 5, 4, ?, 3, 2
: Question mark at index: 2
: Pattern: -1, -1, -1, -1
: New letter: 3
c
$ ./missing-letter -v t t '?' t t
: Translated: 20, 20, ?, 20, 20
: Question mark at index: 2
: Pattern: 0, 0, 0, 0
: New letter: 20
t
$ ./missing-letter -v '?' a c e g
: Translated: ?, 1, 3, 5, 7
: Question mark at index: 0
: Pattern: 2, 2, 2, 2
: New letter: -1
_
The last one illustrates what happens if we try to break out of the lowercase alphabet. It is probably ok; garbage in, garbage out.
Note that e is mising in the definition of a, b and d. And that a
is missing in the definition of c and e. This is a potential problem:
$ ./missing-letter -v '?' b c d e
: Translated: ?, 2, 3, 4, 5
: Question mark at index: 0
: Pattern: 1, 1, 1, 1
: New letter: 1
a
$ ./missing-letter -v '?' b c d x
: Translated: ?, 2, 3, 4, 24
: Question mark at index: 0
: Pattern: 1, 20, 1, 20
: New letter: 1
a
The problem is the double definitions of x and y, and that I did not check both (for the one
where we can choose either one). Let us do that. And fix the non-letter problem as well.
#! /usr/bin/env raku
unit sub MAIN (*@seq where @seq.elems == 5
&& @seq.join ~~ /^<[a..z]>*\?<[a..z]>*$/,
:v(:$verbose));
my @trans = @seq.map({ $_ eq '?' ?? '?' !! $_.ord - 'a'.ord + 1 });
my ($a, $b, $c, $d, $e) = @trans;
my $index = @seq.first('?', :k);
my $x;
my $y;
my $letter;
given $index
{
when 0 { $a = $letter = $b - $d + $c; $x = $d - $c; $y = $e - $d; } # [1a]
when 1 { $b = $letter = $a + $d - $c; $x = $d - $c; $y = $e - $d; } # [1b]
when 2 { $c = $letter = $b + $e - $d; $x = $b - $a; $y = $e - $d; } # [1c]
when 3 { $d = $letter = $c + $b - $a; $x = $b - $a; $y = $c - $b; } # [1d]
when 4 { $e = $letter = $d + $c - $b; $x = $b - $a; $y = $c - $b; } # [1e]
}
die "Missing letter is out of bounds." if 26 > $letter < 1; # [2]
die "Pattern error."
unless $a + $x == $b && $b + $y == $c && $c + $x == $d && $d + $y == $e;
# [3]
if $verbose
{
say ": Translated: { @trans.join(", ") }";
say ": Question mark at index: $index";
say ": Pattern: $x, $y, $x, $y";
say ": New letter: $letter";
}
say chr('a'.ord + $letter -1);
[1] I have replaced the question mark with the actual value, with an additional assignment at the start of each row, so that we can use all the values in [3].
[2] Protest if the new letter is out of bonds.
[3] Protest if the pattern does not fit the values.
Some examples that did not fail with the original program:
$ ./missing-letter-fixed -v '?' a c e g
Missing letter is out of bounds.
in sub MAIN at ./missing-letter-fixed line 25
in block <unit> at ./missing-letter-fixed line 1
$ ./missing-letter-fixed -v '?' b c d x
Pattern error.
in sub MAIN at ./missing-letter-fixed line 27
in block <unit> at ./missing-letter-fixed line 1
Input: @nums = (2, 1, 4, 3)
Output: (2, 1), (1, 4), (4, 3), (2, 3)
Subset 1: (2, 1)
Values: 2 + 1 = 3
Positions: 1 + 2 = 3
Subset 2: (1, 4)
Values: 1 + 4 = 5
Positions: 2 + 3 = 5
Subset 3: (4, 3)
Values: 4 + 3 = 7
Positions: 3 + 4 = 7
Subset 4: (2, 3)
Values: 2 + 3 = 5
Positions: 1 + 4 = 5
Example 2:
Input: @nums = (3, 0, 3, 0)
Output: (3, 0), (3, 0, 3)
Subset 1: (3, 0)
Values: 3 + 0 = 3
Positions: 1 + 2 = 3
Subset 2: (3, 0, 3)
Values: 3 + 0 + 3 = 6
Positions: 1 + 2 + 3 = 6
Example 3:
Input: @nums = (5, 1, 1, 1)
Output: (5, 1, 1)
Subset 1: (5, 1, 1)
Values: 5 + 1 + 1 = 7
Positions: 1 + 2 + 4 = 7
Example 4:
Input: @nums = (3, -1, 4, 2)
Output: (3, 2), (3, -1, 4)
Subset 1: (3, 2)
Values: 3 + 2 = 5
Positions: 1 + 4 = 5
Subset 2: (3, -1, 4)
Values: 3 + (-1) + 4 = 6
Positions: 1 + 2 + 3 = 6
Example 5:
Input: @nums = (10, 20, 30, 40)
Output: ()
#! /usr/bin/env raku
unit sub MAIN (*@nums where @nums.elems > 1 && all(@nums) ~~ Int, # [1]
:v(:$verbose));
my @match; # [2]
for (^@nums.elems).combinations(2 .. @nums.elems -1) -> @indices # [3]
{
my $values = @nums[@indices].sum; # [4]
my $positions = @indices.sum + @indices.elems; # [5]
my $equilibrium = $values == $positions; # [6]
@match.push: @nums[@indices] if $equilibrium; # [7]
say ": ({ @nums[@indices].join(",") }) -> I: @indices[] -> V: $values -> \
P: $positions { $equilibrium ?? "match" !! "" }" if $verbose;
}
say @match
?? @match.map({ "(" ~ $_.join(",") ~ ")" }).join(", ") # [8]
!! '()'; # [8a]
[1] Integers only, with at least one of them.
[2] The matches, if any, will end up here.
[3] The subsets must have a minimum length of 2, and a maximum length
of one less than the total length of the input. We use combinations like this to get
all the subsets of the required lengths. We need the indices as well as the values, so we
iterate over the former - as the latter are easy to look up; see [4].
combinations
[4] Get the sum of the actual values.
[5] Get the sum of the positions. The positions start at 1, so we have to compensate for the zero based indices.
[6] Do we have an equilibrium?
[7] If yes, add the values (not the indices) to the result.
[8] Print the result, if any. If none, print an empty set of parens.
Running it:
$ ./subset-equilibrium 2 1 4 3
(2,1), (2,3), (1,4), (4,3)
$ ./subset-equilibrium 3 0 3 0
(3,0), (3,0,3)
$ ./subset-equilibrium 5 1 1 1
(5,1,1)
$ ./subset-equilibrium 3 -1 4 2
(3,2), (3,-1,4)
$ ./subset-equilibrium 10 20 30 40
()
Looking good.
With verbose mode:
$ ./subset-equilibrium -v 2 1 4 3
: (2,1) -> I: 0 1 -> V: 3 -> P: 3 match
: (2,4) -> I: 0 2 -> V: 6 -> P: 4
: (2,3) -> I: 0 3 -> V: 5 -> P: 5 match
: (1,4) -> I: 1 2 -> V: 5 -> P: 5 match
: (1,3) -> I: 1 3 -> V: 4 -> P: 6
: (4,3) -> I: 2 3 -> V: 7 -> P: 7 match
: (2,1,4) -> I: 0 1 2 -> V: 7 -> P: 6
: (2,1,3) -> I: 0 1 3 -> V: 6 -> P: 7
: (2,4,3) -> I: 0 2 3 -> V: 9 -> P: 8
: (1,4,3) -> I: 1 2 3 -> V: 8 -> P: 9
(2,1), (2,3), (1,4), (4,3)
$ ./subset-equilibrium -v 3 0 3 0
: (3,0) -> I: 0 1 -> V: 3 -> P: 3 match
: (3,3) -> I: 0 2 -> V: 6 -> P: 4
: (3,0) -> I: 0 3 -> V: 3 -> P: 5
: (0,3) -> I: 1 2 -> V: 3 -> P: 5
: (0,0) -> I: 1 3 -> V: 0 -> P: 6
: (3,0) -> I: 2 3 -> V: 3 -> P: 7
: (3,0,3) -> I: 0 1 2 -> V: 6 -> P: 6 match
: (3,0,0) -> I: 0 1 3 -> V: 3 -> P: 7
: (3,3,0) -> I: 0 2 3 -> V: 6 -> P: 8
: (0,3,0) -> I: 1 2 3 -> V: 3 -> P: 9
(3,0), (3,0,3)
$ ./subset-equilibrium -v 5 1 1 1
: (5,1) -> I: 0 1 -> V: 6 -> P: 3
: (5,1) -> I: 0 2 -> V: 6 -> P: 4
: (5,1) -> I: 0 3 -> V: 6 -> P: 5
: (1,1) -> I: 1 2 -> V: 2 -> P: 5
: (1,1) -> I: 1 3 -> V: 2 -> P: 6
: (1,1) -> I: 2 3 -> V: 2 -> P: 7
: (5,1,1) -> I: 0 1 2 -> V: 7 -> P: 6
: (5,1,1) -> I: 0 1 3 -> V: 7 -> P: 7 match
: (5,1,1) -> I: 0 2 3 -> V: 7 -> P: 8
: (1,1,1) -> I: 1 2 3 -> V: 3 -> P: 9
(5,1,1)
$ ./subset-equilibrium -v 3 -1 4 2
: (3,-1) -> I: 0 1 -> V: 2 -> P: 3
: (3,4) -> I: 0 2 -> V: 7 -> P: 4
: (3,2) -> I: 0 3 -> V: 5 -> P: 5 match
: (-1,4) -> I: 1 2 -> V: 3 -> P: 5
: (-1,2) -> I: 1 3 -> V: 1 -> P: 6
: (4,2) -> I: 2 3 -> V: 6 -> P: 7
: (3,-1,4) -> I: 0 1 2 -> V: 6 -> P: 6 match
: (3,-1,2) -> I: 0 1 3 -> V: 4 -> P: 7
: (3,4,2) -> I: 0 2 3 -> V: 9 -> P: 8
: (-1,4,2) -> I: 1 2 3 -> V: 5 -> P: 9
(3,2), (3,-1,4)
$ ./subset-equilibrium -v 10 20 30 40
: (10,20) -> I: 0 1 -> V: 30 -> P: 3
: (10,30) -> I: 0 2 -> V: 40 -> P: 4
: (10,40) -> I: 0 3 -> V: 50 -> P: 5
: (20,30) -> I: 1 2 -> V: 50 -> P: 5
: (20,40) -> I: 1 3 -> V: 60 -> P: 6
: (30,40) -> I: 2 3 -> V: 70 -> P: 7
: (10,20,30) -> I: 0 1 2 -> V: 60 -> P: 6
: (10,20,40) -> I: 0 1 3 -> V: 70 -> P: 7
: (10,30,40) -> I: 0 2 3 -> V: 80 -> P: 8
: (20,30,40) -> I: 1 2 3 -> V: 90 -> P: 9
()
And that's it.