Permuted Reversibles
with Raku

by Arne Sommer

Permuted Reversibles with Raku

[195] Published 6. August 2022.

This is my response to The Weekly Challenge #176.

Challenge #176.1: Permuted Multiples

Write a script to find the smallest positive integer x such that x, 2x, 3x, 4x, 5x and 6x are permuted multiples of each other.

For example, the integers 125874 and 251748 are permutated multiples of each other as

251784 = 2 x 125874

and also both have the same digits but in different order.
Output
142857

Narrowing the Scope

We could just go with my $pmt = (1..Inf).grep: *.&is-pm (and implement the is-pm function), but we can narrow down the possible numbers quite a lot.

The permuted multiples of the number have the same number of digits as the original number (as they must have the same individual digits). It follows from this that 6 times the original number cannot lead to a number with additional digits. The only leading digit allowed is 1 (as 1x6 = 6. 2 gives 2x6 = 12 - which has two digits). This is shown as A and B in this illustration:

Note that a leading «17» (and «18» and «19») will also give an additional digit (shown as C in the illustration), but this does not lead to so many values to discard as the first digit rule (discard anything but «1»).

Off we go.

File: permuted-multiples
#! /usr/bin/env raku

unit sub MAIN (Int $count where $count > 0 = 1);        # [1]

my $ptm := (^Inf).map({ "1$_" }).grep( *.&is-pm );      # [2]

say $ptm[^$count].join(", ");

sub is-pm ($number)                                     # [3]
{
  my $n  = $number.comb.sort.join;                      # [4]
  my $n2 = ($number * 2).comb.sort.join;                # [5]
  my $n3 = ($number * 3).comb.sort.join;                # [5]
  my $n4 = ($number * 4).comb.sort.join;                # [5]
  my $n5 = ($number * 5).comb.sort.join;                # [5]
  my $n6 = ($number * 6).comb.sort.join;                # [5]

  return $n eq $n2 eq $n3 eq $n3 eq $n4 eq $n5 eq $n6;  # [6]
}

[1] Print the very first match, unless another number is specified.

[2] ^Inf gives the integer sequence 0,1,2,... . We kan skip integers starting with any other digit than 1, and using map to add a leading 1 to the values in the sequence does that nicely.

[3] Check if the number is a permuted multiple.

[4] Comparing numbers for the same digits can be done by transforming them to a canonical form (i.e. sorted), before the comparison. Then they would be the same.

[5] Compute the 2x, 3x, 4x and 5x. Then transform them to canonical form.

[6] We have a Permuted Multiple if all the canonical forms are identical.

Running it:

$ ./permuted-multiples
142857

Looking good.

More values?

$ ./permuted-multiples 3
142857, 1428570, 1429857

But we have a problem.

The algorithm added a leading «1» before the value it got from the sequence ^Inf. The sequence gives us the values 0, 1, 2, and so on. The problem is that we have missed quite a few numbers, e.g. 100, 101, 102, 1000, 1001, 1010. (Actually every integer where the second digit is «0», except the number «10».)

So much for clever optimisation...

File: permuted-multiples-fixed
#! /usr/bin/env raku

unit sub MAIN (Int $count where $count > 0 = 1);

my $ptm := (^Inf).grep({ $_.starts-with('1') && $_.&is-pm });  # [1]

say $ptm[^$count].join(", ");

sub is-pm ($number)
{
  my $n  = $number.comb.sort.join;
  my $n2 = ($number * 2).comb.sort.join;
  my $n3 = ($number * 3).comb.sort.join;
  my $n4 = ($number * 4).comb.sort.join;
  my $n5 = ($number * 5).comb.sort.join;
  my $n6 = ($number * 6).comb.sort.join;

  return $n eq $n2 eq $n3 eq $n3 eq $n4 eq $n5 eq $n6;
}

[1] Done right, this time. Note the use of starts-with instead of a (slower) regex to check if the string (coerced from numeric form) starts with the digit «1».

See docs.raku.org/routine/starts-with for more information about starts-with.

We get the same result:

 $ ./permuted-multiples-fixed
142857

$ ./permuted-multiples-fixed 3
142857, 1428570, 1429857

Note that asking for e.g. 10 values will take a very long time, and nothing will be printed before all ten values have been computed. This is an inherent property of sequences. (Or at least, this type of sequence.)

$ ./permuted-multiples-fixed 10
142857, 1428570, 1429857, 14285700, 14298570, 14299857, 142857000, 142985700, 142998570, 142999857

We can work around it, e.g. like this:

File: permuted-multiples-unbuffered
#! /usr/bin/env raku

unit sub MAIN (Int $count where $count > 0 = 1);

my $ptm := (^Inf).grep({ $_.starts-with('1') && $_.&is-pm });

for ^$count -> $index
{
  my $val = $ptm[$index];
  print ", " if $index;   # [1]
  print $val;
}

say "";                   # [1a]

sub is-pm ($number)
{
  my $n  = $number.comb.sort.join;
  my $n2 = ($number * 2).comb.sort.join;
  my $n3 = ($number * 3).comb.sort.join;
  my $n4 = ($number * 4).comb.sort.join;
  my $n5 = ($number * 5).comb.sort.join;
  my $n6 = ($number * 6).comb.sort.join;

  return $n eq $n2 eq $n3 eq $n3 eq $n4 eq $n5 eq $n6;
}

[1] Note the use of print to get the values without a trailing newline. Also note the say after the loop to get the final newline.

See docs.raku.org/routine/print for more information about print.

See docs.raku.org/routine/say for more information about say.

The output is the same, but it will print the values as soon as they are computed.

Challenge #176.2: Reversible Numbers

Write a script to find out all Reversible Numbers below 100.

A number is said to be a reversible if sum of the number and its reverse had only odd digits.

For example
36 is reversible number as 36 + 63 = 99 i.e. all digits are odd.
17 is not reversible as 17 + 71 = 88, none of the digits are odd.
Output
10, 12, 14, 16, 18, 21, 23, 25, 27,
30, 32, 34, 36, 41, 43, 45, 50, 52,
54, 61, 63, 70, 72, 81, 90
File: reversible-numbers
#! /usr/bin/env raku

unit sub MAIN (Int $limit where $limit > 0 = 100);       # [1]

say (10 .. $limit).grep( *.&is-reversible ).join(", ");  # [2]

sub is-reversible ($number)                              # [2a]
{
  my $sum = $number + $number.flip;                      # [3]

  return $sum !~~ /<[02468]>/;                           # [4]
}

[1] Note that this time we get the upper limit (of the values to consider), and not the number of values we want.

[2] We cannot have a reversible number with only one digit, as the that would give an even sum (the same as 2 times the number). So we start at 10.

[3] Get the sum of the number and the number reversed, which we get with flip. (Note that flip works on strings, as reverse works on lists only).

See docs.raku.org/routine/flip for more information about flip.

See docs.raku.org/routine/reverse for more information about reverse.

[4]. We have a Reversible Number if the sum (from [3]) does not contain any even digits (i.e. they are all odd).

Running it:

$ ./reversible-numbers
10, 12, 14, 16, 18, 21, 23, 25, 27, 30, 32, 34, 36, 41, 43, 45, 50, 52, 54,\
  61, 63, 70, 72, 81, 90

Looking good.

And that's it.