This is my response to The Weekly Challenge #361.
Input: $int = 4
Output: 3,1
4 => 3 + 1 (non-consecutive fibonacci numbers)
Example 2:
Input: $int = 12
Output: 8,3,1
12 => 8 + 3 + 1
Example 3:
Input: $int = 20
Output: 13,5,2
20 => 13 + 5 + 2
Example 4:
Input: $int = 96
Output: 89,5,2
96 => 89 + 5 + 2
Example 5:
Input: $int = 100
Output: 89,8,3
100 => 89 + 8 + 3
! /usr/bin/env raku
unit sub MAIN (Int $int where 1 <= $int <= 100, # [1]
:v(:$verbose));
my $fib := (1, 1, * + * ... Inf); # [2]
my @rev; # [3]
for $fib -> $f # [4]
{
last if $f > $int; # [4a]
@rev.unshift: $f; # [4b]
}
sink @rev.pop; # [5]
say ": Reverse fib: { @rev.join(", ") }" if $verbose;
recurse( (), @rev); # [6]
sub recurse(@zeckendorf is copy, @rev is copy) # [6a]
{
say ": Z:{ @zeckendorf.join(",") } R:{ @rev.join(",") }" if $verbose;
if @zeckendorf.sum == $int # [7]
{
say @zeckendorf.join(","); # [7a]
exit; # [7b]
}
return if @zeckendorf.sum > $int; # [8]
return unless @rev.elems; # [9]
my $next = @rev.shift; # [10
recurse(@zeckendorf, @rev); # [110]
@zeckendorf.push: $next; # [121]
sink @rev.shift if @rev.elems; # [132]
recurse(@zeckendorf, @rev) # [143]
}
[1] Ensure an integer in the range 1 .. 100, both included.
[2] The lazy Fibonacci sequence, in Raku fashion.
The Fibonacci sequence usually starts with 0,1,1,2,3 (A) or 1,1,2.3 (B). Both of these give the same result for us, as zero does not add up to anything. The sequence may alternatively start with 1,2,3 (C), though this is more unusual, according to en.wikipedia.org/wiki/Fibonacci_sequence. I had to choose the (C) version, as the Zeckendorf Representation is unique and the number 3 would cause both 3 (correct) and 1+2 (as in 1,1,2) (wrong) as solutions for the (A) and (B) sequence variations.
[3] The reverse Fibonacci sequence will end up here, up to (and including) the input integer.
[4] Iterate over the lazy sequence, until we reach the input
number itself [4a]. Add the current value to the front of the reversed list
with unshift.
See docs.raku.org/routine/unshift for more information about unshift.
[5]
Get rid of the first 1, by removing the last element
with pop. sink is there to explicitly throw it away.
See docs.raku.org/routine/pop for more information about pop.
See docs.raku.org/routine/sink for more information about sink.
[6] Off we go, recursively. The first argument is the partial result, and the second is the values not done so far.
[7] Do we have the value? If so, print the list [7a] and exit [7b].
[8] Abandon this recursion if we have a too high sum,
[9] or we have no more values to consider.
[10] Get the next value to consider.
[11] Ignore it for now, and recursively go on.
[12] Add the value we ignored above, and go on.
[13] Get rid of the next value (with shift), as
non-consecutive values are illegal.
See docs.raku.org/routine/shift for more information about shift.
unshift (add at start), (remove at end)
and shift (remove at start) are three of the four possibilites. The
missing one is push (add at end).
See docs.raku.org/routine/push for more information about push.
[13] Recursively go on, with the previously ignored value.
Running it:
$ ./zeckendorf-representation 4
3,1
$ ./zeckendorf-representation 12
8,3,1
$ ./zeckendorf-representation 20
13,5,2
$ ./zeckendorf-representation 96
89,5,2
$ ./zeckendorf-representation 100
89,8,3
Looking good.
The two first ones only with verbose mode:
$ ./zeckendorf-representation -v 4
: Reverse fib: 3, 2, 1
: Z: R: 3,2,1
: Z: R: 2,1
: Z: R: 1
: Z: R:
: Z:1 R:
: Z:2 R:
: Z:3 R: 1
: Z:3 R:
: Z:3,1 R:
3,1
$ ./zeckendorf-representation -v 12
: Reverse fib: 8, 5, 3, 2, 1
: Z: R: 8,5,3,2,1
: Z: R: 5,3,2,1
: Z: R: 3,2,1
: Z: R: 2,1
: Z: R: 1
: Z: R:
: Z:1 R:
: Z:2 R:
: Z:3 R: 1
: Z:3 R:
: Z:3,1 R:
: Z:5 R: 2,1
: Z:5 R: 1
: Z:5 R:
: Z:5,1 R:
: Z:5,2 R:
: Z:8 R: 3,2,1
: Z:8 R: 2,1
: Z:8 R: 1
: Z:8 R:
: Z:8,1 R:
: Z:8,2 R:
: Z:8,3 R: 1
: Z:8,3 R:
: Z:8,3,1 R:
8,3,1
The third example gives 43 rows of output, the fourth gives 199 and the fifth and last one gives 206. Too much to print here I think.
Input: @party = (
[0, 0, 0, 0, 1, 0], # 0 knows 4
[0, 0, 0, 0, 1, 0], # 1 knows 4
[0, 0, 0, 0, 1, 0], # 2 knows 4
[0, 0, 0, 0, 1, 0], # 3 knows 4
[0, 0, 0, 0, 0, 0], # 4 knows NOBODY
[0, 0, 0, 0, 1, 0], # 5 knows 4
);
Output: 4
Example 2:
Input: @party = (
[0, 1, 0, 0], # 0 knows 1
[0, 0, 1, 0], # 1 knows 2
[0, 0, 0, 1], # 2 knows 3
[1, 0, 0, 0] # 3 knows 0
);
Output: -1
Example 3:
Input: @party = (
[0, 0, 0, 0, 0], # 0 knows NOBODY
[1, 0, 0, 0, 0], # 1 knows 0
[1, 0, 0, 0, 0], # 2 knows 0
[1, 0, 0, 0, 0], # 3 knows 0
[1, 0, 0, 0, 0] # 4 knows 0
);
Output: 0
Example 4:
Input: @party = (
[0, 1, 0, 1, 0, 1], # 0 knows 1, 3, 5
[1, 0, 1, 1, 0, 0], # 1 knows 0, 2, 3
[0, 0, 0, 1, 1, 0], # 2 knows 3, 4
[0, 0, 0, 0, 0, 0], # 3 knows NOBODY
[0, 1, 0, 1, 0, 0], # 4 knows 1, 3
[1, 0, 1, 1, 0, 0] # 5 knows 0, 2, 3
);
Output: 3
Example 5:
Input: @party = (
[0, 1, 1, 0], # 0 knows 1 and 2
[1, 0, 1, 0], # 1 knows 0 and 2
[0, 0, 0, 0], # 2 knows NOBODY
[0, 0, 0, 0] # 3 knows NOBODY
);
Output: -1
Example 6:
Input: @party = (
[0, 0, 1, 1], # 0 knows 2 and 3
[1, 0, 0, 0], # 1 knows 0
[1, 1, 0, 1], # 2 knows 0, 1 and 3
[1, 1, 0, 0] # 3 knows 0 and 1
);
Output: -1
#! /usr/bin/env raku
unit sub MAIN ($string = "1 0 0 | 0 0 1 | 1 0 0", # [1]
:v(:$verbose));
my $matrix = $string.split("|")>>.words>>.Numeric>.Array; # [2]
my $rows = $matrix.elems; # [3]
my $cols = $matrix[0].elems; # [4]
die "The rows must have the same size"
unless [==] $matrix>>.elems; # [5]
die "The matrix must have the same number of rows and columns"
unless $rows == $cols; # [6]
die "Must contain 0s and 1s only"
unless all($matrix[*;*]) ~~ one(0,1); # [7]
say ":Matrix: { $matrix.raku }" if $verbose;
for ^$cols -> $col # [8]
{
my @col = $matrix[*;$col]; # [9]
say ":Column $col: @col[]" if $verbose;
if @col.sum == $cols -1 # [10]
{
say ":Column $col is a match" if $verbose;
# my $index = @col.grep(0, :k); # [11]
if $matrix[$col;*].sum == 0 # [12]
{
say $index; # [12a]
exit; # [12b]
}
}
}
say '-1'; # [13]
[1] Note my «matrix as a string» input format, as the default value.
[2] Turn the string into a two dimentional array. Coerce (all) the
values into numeric values (as verbose mode would expose the IntStr
or NumStr types), and unlazify the result by turning it into an array
(from a sequence). The integer check will be done later on (in [7]).
[3] The number of rows.
[4] The number of columns, taken from the first row.
[5] All the rows must have the same size (length).
[6] The number of rows and columns must be the same.
[7] Flatten the matrix with [*;*] and make sure that all the
values are one of 0 or 1.
[8] Iterate over the column indices.
[9] Get the column data.
[10] Do everybody except one row know this one?
[11] Get the cell with the zero, with grep. The
normal behaviour is to return the value, but the :k (key)
adverb gives the index instead. But we do not actually need this index,
as we already have it in $col and that one will ensure that the value
at this index is zero (as the whole row must be zero) in [12].
See docs.raku.org/routine/grep for more information about grep.
[12] Then we check that this cell does not know anybody. If so, print the index [12a] and and exit [12b].
[13] No match? Print the error message -1.
Running it:
$ ./find-celebrity "0 0 0 0 1 0 | 0 0 0 0 1 0 | 0 0 0 0 1 0 | 0 0 0 0 1 0 | \
0 0 0 0 0 0 | 0 0 0 0 1 1"
4
$ ./find-celebrity "0 1 0 0 | 0 0 1 0 | 0 0 0 1 | 1 0 0 0"
-1
$ ./find-celebrity "0 0 0 0 0 | 1 0 0 0 0 | 1 0 0 0 0 | 1 0 0 0 0 | 1 0 0 0 0"
0
$ ./find-celebrity "0 1 0 1 0 1 | 1 0 1 1 0 0 | 0 0 0 1 1 0 | \
0 0 0 0 0 0 | 0 1 0 1 0 0 | 1 0 1 1 0 0 "
3
$ ./find-celebrity "0 1 1 0 | 1 0 1 0 | 0 0 0 0 | 0 0 0 0"
-1
$ ./find-celebrity "0 0 1 1 | 1 0 0 0 | 1 1 0 1 | 1 1 0 0"
-1
Looking good.
With verbose mode:
$ ./find-celebrity -v "0 0 0 0 1 0 | 0 0 0 0 1 0 | 0 0 0 0 1 0 | \
0 0 0 0 1 0 | 0 0 0 0 0 0 | 0 0 0 0 1 1"
:Matrix: $([0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 1, 0], \
[0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1])
:Considering column 0: 0 0 0 0 0 0
:Considering column 1: 0 0 0 0 0 0
:Considering column 2: 0 0 0 0 0 0
:Considering column 3: 0 0 0 0 0 0
:Considering column 4: 1 1 1 1 0 1
:Everybodey knows 4
:and 4 does not know anybody
4
$ ./find-celebrity -v "0 1 0 0 | 0 0 1 0 | 0 0 0 1 | 1 0 0 0"
:Matrix: $([0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0])
:Considering column 0: 0 0 0 1
:Considering column 1: 1 0 0 0
:Considering column 2: 0 1 0 0
:Considering column 3: 0 0 1 0
-1
$ ./find-celebrity -v "0 0 0 0 0 | 1 0 0 0 0 | 1 0 0 0 0 | \
1 0 0 0 0 | 1 0 0 0 0"
:Matrix: $([0, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], \
[1, 0, 0, 0, 0], [1, 0, 0, 0, 0])
:Considering column 0: 0 1 1 1 1
:Everybodey knows 0
:and 0 does not know anybody
0
$ ./find-celebrity -v "0 1 0 1 0 1 | 1 0 1 1 0 0 | 0 0 0 1 1 0 | \
0 0 0 0 0 0 | 0 1 0 1 0 0 | 1 0 1 1 0 0 "
:Matrix: $([0, 1, 0, 1, 0, 1], [1, 0, 1, 1, 0, 0], [0, 0, 0, 1, 1, 0], \
[0, 0, 0, 0, 0, 0], [0, 1, 0, 1, 0, 0], [1, 0, 1, 1, 0, 0])
:Considering column 0: 0 1 0 0 0 1
:Considering column 1: 1 0 0 0 1 0
:Considering column 2: 0 1 0 0 0 1
:Considering column 3: 1 1 1 0 1 1
:Everybodey knows 3
:and 3 does not know anybody
3
$ ./find-celebrity -v "0 1 1 0 | 1 0 1 0 | 0 0 0 0 | 0 0 0 0"
:Matrix: $([0, 1, 1, 0], [1, 0, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0])
:Considering column 0: 0 1 0 0
:Considering column 1: 1 0 0 0
:Considering column 2: 1 1 0 0
:Considering column 3: 0 0 0 0
-1
$ ./find-celebrity -v "0 0 1 1 | 1 0 0 0 | 1 1 0 1 | 1 1 0 0"
:Matrix: $([0, 0, 1, 1], [1, 0, 0, 0], [1, 1, 0, 1], [1, 1, 0, 0])
:Considering column 0: 0 1 1 1
:Everybodey knows 0
:Considering column 1: 0 0 1 1
:Considering column 2: 1 0 0 0
:Considering column 3: 1 0 1 0
-1
And that's it.