Perl Weekly Challenge: Week 141

As I write this, we are already into the Advent of Code. Once again, I am doing it in Raku, inspired by Daniel "codesections" Sockwell. My solutions for the first five days are available on github in the Advent of Raku 2021 repository.

Challenge 1:

Number Divisors

Write a script to find lowest 10 positive integers having exactly 8 divisors.

Example
24 is the first such number having exactly 8 divisors.
1, 2, 3, 4, 6, 8, 12 and 24.

Finding the divisors is quite simple in Raku. Treating the number as a range from 1 to the number itself, we just look for numbers in the range that divide without a remainder. If there are eight such numbers, hasEightDivisors() return True otherwise, False.

sub hasEightDivisors(Int $n) {
    return (1 .. $n).grep({ $n %% $_; }).elems == 8;
}

We have to find the first 10 of these numbers with 8 divisors. As 1 is the only positive integer with 1 divisor, we start with 2 and just keep counting upwards, running hasEightDivisors() on each successive number until we have found 10. Then we print them out.

sub MAIN() {

    my @eightDivisors;
    my $n = 2;

    while @eightDivisors.elems < 10 {
        if hasEightDivisors($n) {
            @eightDivisors.push($n);
        }
        $n++;
    }

    say join q{, }, @eightDivisors;
}

(Full code on Github.)

This is the Perl version. It works the same way as Raku albeit in a more verbose fashion.

sub hasEightDivisors {
    my ($n) = @_;
    my $count = 0;

    for my $i (1 .. $n) {
        if ($n % $i == 0) {
            $count++;
        }
    }

    return $count == 8;
}

my @eightDivisors;
my $n = 2;

while (scalar @eightDivisors < 10) {
    if (hasEightDivisors($n)) {
        push @eightDivisors, $n;
    }
    $n++;
}

say join q{, }, @eightDivisors;

(Full code on Github.)

Challenge 2:

Like Numbers

You are given positive integers, $m and $n.

Write a script to find total count of integers created using the digits of $m which is also divisible by $n.

Repeating of digits are not allowed. Order/Sequence of digits can’t be altered. You are only allowed to use (n-1) digits at the most. For example, 432 is not acceptable integer created using the digits of 1234. Also for 1234, you can only have integers having no more than three digits.

Example 1
Input: $m = 1234, $n = 2
Output: 9

Possible integers created using the digits of 1234 are:
1, 2, 3, 4, 12, 13, 14, 23, 24, 34, 123, 124, 134 and 234.

There are 9 integers divisible by 2 such as:
2, 4, 12, 14, 24, 34, 124, 134 and 234.
Example 2
Input: $m = 768, $n = 4
Output: 3

Possible integers created using the digits of 768 are:
7, 6, 8, 76, 78 and 68.

There are 3 integers divisible by 4 such as:
8, 76 and 68.

I wasted a lot of time on this one building an elaborate system of loops within loops and then it struck me; I didn't need any of that! In fact, the problem can be solved in one line of Raku.

First $m is split into its' individual digits.

    $m.comb

.combinations(), as the name says, provides all the different combinations of those digits. I knew about this method but initially I rejected it because I thought it wouldn't be able to deal with invalid combinations such as 432 but it turns out it can. The parameter restrics the size of the combinations from one to one less than the number of digits as per the spec.

        .combinations(1 ..^ $m.chars)

.combinations() returns a list of lists. .map() and .join() are used to turn this back into a list of integers.

        .map({ $_.join; })

From that list, we select the numbers which are evenly divisible by $n.

        .grep({ $_ %% $n; })

The quantity of those numbers is counted:

        .elems

...and that value is printed out.

        .say;

(Full code on Github.)

The Perl version also starts by splitting $m into digits.

my @digits = split //, $m;

The next two steps from the Raku version have to be done differently because the combinations() routine I used in previous challenges doesn't quite work the same as the equivalent Raku method.

my @combinations;

for my $i (1 .. scalar @digits - 1) {
    push @combinations, map { join q{}, @{$_}; } combinations(\@digits, $i);
}

But it results in a list of integers just the same. This can be grep()ed for numbers which are evenly divisible by $n, counted and printed.

say scalar grep { $_ % $n == 0; } @combinations;

(Full code on Github.)