Disarmed Ranking
with Raku

by Arne Sommer

Disarmed Ranking with Raku

[193] Published 23. July 2022.

This is my response to The Weekly Challenge #174.

Challenge #174.1: Disarium Numbers

Write a script to generate first 19 Disarium Numbers.

A disarium number is an integer where the sum of each digit raised to the power of its position in the number, is equal to the number.

For example:
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.

Challenge #174.2: Permutation Ranking

You are given a list of integers with no duplicates, e.g. [0, 1, 2].

Write two functions, 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]
File: permutation-ranking
#! /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.