Perl Weekly Challenge: Week 170

Challenge 1:

Primorial Numbers

Write a script to generate first 10 Primorial Numbers.

Primorial numbers are those formed by multiplying successive prime numbers.

For example,

P(0) = 1    (1)
P(1) = 2    (1x2)
P(2) = 6    (1x2×3)
P(3) = 30   (1x2×3×5)
P(4) = 210  (1x2×3×5×7)

This can be computed in Raku as a one-liner.

my $n = 1; (1, |(0 .. *).grep({ .is-prime })[^9]).map({ $n *= $_ }).join(q{, }).say

(Full code on Github.)

First we set up a variable $n which will hold the current running total of multiplied primes (i.e. the current primorial number.)

Then we set up an list primed (haha! get it?) with 1 because apparently 1 is the first primorial number even though it isn't a prime by most definitions.

(0 .. *).grep({ .is-prime })[^9] uses a lazy list to give us the next 9 prime numbers. The | before this is to flatten the results so we can add them as individual elements to our primorial list.

.map({ $n *= $_ }) takes $n and multiplies it by each member of the list which gives us a list of 10 primorials.

Then the standard .join(q{, }).say is used to print out the results in a nice way.

For Perl I had to break out my veteran isPrime() and nextPrime() functions and instead of grep() and map(), I used a loop to create the list of Primorials.

my @primorials = (1);
my $n = 1;

until (scalar @primorials == 10) {
    push @primorials, $n *= nextPrime();
}

say join q{, }, @primorials;

(Full code on Github.)

In case you wanted to know, the first 10 primorials are 1, 2, 6, 30, 210, 2310, 30030, 510510, 9699690 and 223092870.

Challenge 2:

Kroneker Product

You are given 2 matrices.

Write a script to implement Kronecker Product on the given 2 matrices.

For more information, please refer to the wikipedia page.

For example,

A = [ 1 2 ]
    [ 3 4 ]

B = [ 5 6 ]
    [ 7 8 ]

A x B = [ 1 x [ 5 6 ]   2 x [ 5 6 ] ]
        [     [ 7 8 ]       [ 7 8 ] ]
        [ 3 x [ 5 6 ]   4 x [ 5 6 ] ]
        [     [ 7 8 ]       [ 7 8 ] ]

    =   [ 1x5 1x6 2x5 2x6 ]
        [ 1x7 1x8 2x7 2x8 ]
        [ 3x5 3x6 4x5 4x6 ]
        [ 3x7 3x8 4x7 4x8 ]

    =   [  5  6 10 12 ]
        [  7  8 14 16 ]
        [ 15 18 20 24 ]
        [ 21 24 28 32 ]

Lucky for me, my daughter Shailaja has taken linear algebra recently enough that she still remembers this stuff. Once I was up to speed, this was rather easy in Raku. Even so, I hard-coded the two matrices from the example rather than allowing for arbitrary input.

printMatrix((@A X @B).map({ [X*] $_ }));

@A X @B gives the cross product of the two arrays. In orther words it produces an array where each element is an array consisting of one element from @A and one element from @B. Then .map({ [X*] $_ }) multiplies the elements of each of those arrays together. The result is a matrix that contains the Kroneker Product of @A and @B.

printMatrix() is just a small function to pretty-print a two-dimensional array of integers like our matrices.

sub printMatrix(@matrix) {
    for @matrix -> @row {
        for @row -> $col {
            $col.fmt('%2d ').print;
        }
        print "\n";
    }
}

(Full code on Github.)

I am not that happy with my Perl version. For a start, deprived of all those fancy Raku operators, it uses 4-level nested loops which can't be good with large matrices from an efficiency point of view. It works well enough for the sample input though.

sub kroneckerProduct {
    my @A = @{$_[0]};
    my @B = @{$_[1]};
    my @answer;

    for my $a (0 .. scalar @A - 1) {
        for my $aa (@{$A[$a]}) {
            for my $b (0 .. scalar @B - 1) {
                for my $bb (@{$B[$b]}) {

Also the line below for ensuring the results get put in the right place in the output matrix feels hacky to me. If I had a 3 x 3 matrix, would it be enough to change it to $a * 3 or would some more complex calculation be needed? Again, what I have works for the task at hand but one day I really should try and generalize this.

                    push @{$answer[$a * 2 + $b]}, $aa * $bb;
                }
            }
        }
    }

    return \@answer;
}

The Perl version of printMatrix() looks like this.

sub printMatrix {
    my @matrix =  @{$_[0]};
    for my $row (@matrix) {
        for my $col (@{$row}) {
            printf '%2d ', $col;
        }
        print "\n";
    }
}

printMatrix(kroneckerProduct(\@A, \@B));

(Full code on Github.)