Perl Weekly Challenge: Week 252

Challenge 1:

Special Numbers

You are given an array of integers, @ints.

Write a script to find the sum of the squares of all special elements of the given array.

An element $int[i] of @ints is called special if i divides n, i.e. n % i == 0.
Where n is the length of the given array. Also the array is 1-indexed for the task.
Example 1
Input: @ints = (2, 7, 1, 19, 18, 3)
Output: 63

There are exactly 4 special elements in the given array:
$ints[1] since 1 divides 6,
$ints[2] since 2 divides 6,
$ints[3] since 3 divides 6, and
$ints[6] since 6 divides 6.

Hence, the sum of the squares of all special elements of given array:
2 * 2 + 7 * 7 + 1 * 1 + 3 * 3 = 63
Example 2
Input: @ints = (1, 2, 3, 4)
Output: 21

There are exactly 3 special elements in the given array:
$ints[1] since 1 divides 4,
$ints[2] since 2 divides 4, and
$ints[4] since 4 divides 4.

Hence, the sum of the squares of all special elements of given array:
1 * 1 + 2 * 2 + 4 * 4 = 21.

In order to solve this challenge we need to know the factors of the length of @ints. Fortunately, I had some code for this I could reuse from a previouse challenge. Here it is again.

factors() takes an integer $n as parameter. Then from 1 to half the value of $n, we look using .grep() for numbers which divide evenly into #n using the %% operator. For example 1 where the length of @ints is 6, the function as described would return 1, 2, and 3. This was sufficient for its' original use but in this challenge we also need to include $n itself. So it is tacked on to the results of the previous computation and the two are "flattened" into one array with the | operator. This is returned by the function.

sub factors(Int $n) {
    return  (| (1 .. $n div 2).grep({ $n %% $_ }), $n);
}

Now we can solve the challenge in one line. factors() finds the factors of the length of @ints (found with .elems()). These factors are used via .map() as indices into @ints (minus 1 because arrays are 0-based not 1-based) and the element at that index is squared. The squares are then added together with .sum() and the result is printed with .say().

factors(@ints.elems).map({ @ints[$_ - 1] ** 2 }).sum.say;

(Full code on Github.)

For Perl, we need a replacement for .sum(); it looks like this.

sub sum {
    my $total;
    for my $n (@_) {
        $total += $n;
    }

    return $total;
}

And the rest of the code follows the Raku version.

sub factors {
    my ($n) = @_;

    return grep { $n % $_ == 0; } 1 .. $n / 2, $n;
}


say sum(map { $ARGV[$_ - 1] ** 2 } factors(scalar @ARGV));

(Full code on Github.)

Challenge 2:

Unique Sum Zero

You are given an integer, $n.

Write a script to find an array containing $n unique integers such that they add up to zero.

Example 1
Input: $n = 5
Output: (-7, -1, 1, 3, 4)

Two other possible solutions could be as below:
(-5, -1, 1, 2, 3) and (-3, -1, 2, -2, 4).
Example 2
Input: $n = 3
Output: (-1, 0, 1)
Example 3
Input: $n = 1
Output: (0)

This challenge looks harder than it actually was. There are many different ways to get a suitable array but I thought of an extremely simple method. We know an integer and its' negative counterpart added together equals 0. So it seemed to me all we needed to do is take the numbers from 1 to half the value of $n and add them to an array then negate each one and add those to the array. Then the sum of the elements will always equal 0.

my @results = (1 .. $n div 2).map({ | ($_, -$_) });

This works great if the value of $n is even. But if it is odd, there is a fly in the ointment. Because we are using the integer division operator div to halve $n the resulting range will be short one value. We deal with this problem by simply adding 0 to the results if $n is odd.

unless $n %% 2 {
    @results.push(0);
}

Finally, the results are output in the format of the examples.

say q{(}, @results.sort.join(q{, }), q{)};

(Full code on Github.)

This is the Perl version.

my $n = shift; 
my @results = map { $_, -$_ } (1 .. $n / 2);

unless ($n % 2 == 0) {
    push @results, 0;
}

say q{(}, (join q{, }, sort @results), q{)};

(Full code on Github.)