This is my response to The Weekly Challenge #174.
19 Disarium Numbers
.
518 is a disarium number as (5 ** 1) + (1 ** 2) + (8 ** 3) => 5 + 1 + 512 => 518
This sequence is available as oeis.org/A032799 - elbeit without using the name «Disarium Numbers».
File: disarium-sequence
#! /usr/bin/env raku
unit sub MAIN (Int $count where $count > 0 = 19); # [1]
my $ds := (^Inf).grep( *.&is-disarium ); # [2]
say $ds[^$count].join(", "); # [3]
sub is-disarium ($number) # [2a]
{
my $position = 0; # [4]
my $sum = 0; # [5]
for $number.comb -> $digit # [6]
{
$sum += $digit ** ++$position; # [7]
}
return $sum == $number; # [8]
}
[1] The number of values to print, as a positive integer, with 19 as default.
[2] The sequence, starting with all non-negative integers, and applying grep
to keep the disarium numbers only.
[3] Print the requested number of values.
[4] The position, as we need this in the calculation. The first one has position 1, and not zero (the offset). I have compensated for this with the prefix incrementation in [7]
[5] The total sum.
[6] For each digit,
[7] • add the sum of that digit (raised to the power of the position).
[8] Do we have a match (i.e. the sum is the same as the number)?
Running it:
$ ./disarium-sequence 19
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175, 518, 598, 1306, 1676, 2427, \
2646798
We got the same sequence as OEIS (see link above), so we are good.
[0, 1, 2]
.
permutation2rank()
which will take the list and
determine its rank (starting at 0) in the set of possible permutations arranged in
lexicographic order, and rank2permutation()
which will take the list
and a rank number and produce just that permutation.
Please checkout this post for more informations and algorithm.
Given the list[0, 1, 2]
the ordered permutations are:
0: [0, 1, 2]
1: [0, 2, 1]
2: [1, 0, 2]
3: [1, 2, 0]
4: [2, 0, 1]
5: [2, 1, 0]
and therefore:
permutation2rank([1, 0, 2]) = 2
rank2permutation([0, 1, 2], 1) = [0, 2, 1]
#! /usr/bin/env raku
unit sub MAIN (*@i where all(@i) ~~ Int && ! @i.repeated, # [1]
:$r, :v(:$verbose)); # [1a]
say permutation2rank(@i); # [2]
say rank2permutation(@i, $r); # [6]
sub permutation2rank(@list) # [2a]
{
my @p = @list.sort.permutations; # [3]
for ^@p.elems -> $index # [4]
{
say ": $index -> @p[$index]" if $verbose;
return $index if @p[$index] cmp @list == 0; # [5]
}
}
sub rank2permutation(@list, $index) # [6a]
{
my @p = @list.sort.permutations; # [7]
return @p[$index]; # [8]
}
[1] The all
Juntion is used to enforce
that all the arguments are integers, and the repeated
is used
to ensure that they are unique. The function returns any duplictes, and
negating that list gives the desired result. Note the use of a named argument
(:$r
) to specify the rank value [1a].
[2] Give us the rank.
[3] We start with the permutations, after sorting the list so that we get them in the correct order.
See
docs.raku.org/routine/permutations for more information about permutations
.
[4] Iterate over the index of the permutations.
[5] Return the index if we have found the correct version of the list.
Note the use of cnp
to smart match two objects. The return value is 0 (when
coerced to a numeric value, as done here) if they are equal.
See
docs.raku.org/routine/cmp
for more information about the Generic, "smart" three-way comparator
cmp
.
[6] Get the permutation with the given rank.
[7] The same as [3].
[8] Look it up with the index - which is the same as the rank.
Running it:
$ ./permutation-ranking -r=1 1 0 2
2
(0 2 1)
Looking good.
And that's it.