Sum enchanted evening… you’ll see a special number
You’ll see a special number, and you’ll sum its squares.
And somehow you know, you know even then,
That somehow you’re working on Perl Weekly Challenge 252!
Task 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 = (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.
Example 2
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
Approach
Well, if you’ve been following me, you know I’m going to break this up into smaller functions. I’m also borrowing code I wrote for Perl Weekly Challenge 229.
Raku
sub specialElementIndices($n) {
return (1 .. $n).grep({ $n % $_ == 0 });
}
# code borrowed from my code for PWC 229
sub english_list ( *@list ) {
# given a list, join it in a way that makes sense
# to english speakers
my $last = @list.pop(); # last element in array
if (@list == 0) {
# using an array in a scalar context returns
# the number of elements in the array
# there was only one element in the list
return $last;
}
my $joined = @list.join(qq{,\n});
if (@list > 1) {
# if there's more than element, add an Oxford comma
$joined ~= q{,};
}
return "$joined and\n$last";
}
sub specialNumberSquareSum(@ints) {
my $n = @ints.elems;
# find the list of indices for "special" numbers
my @specialIndices = specialElementIndices($n);
my $count = @specialIndices.elems;
my @explain_list = @specialIndices.map({
"\$ints[$_] since $_ divides $n"
});
my $explain = "There are exactly $count special elements "
~ "in the given array:\n" ~ english_list(@explain_list);
# find the special numbers themselves
my @special = @specialIndices.map({ @ints[$_ - 1] });
# find the sum of the squares
my $sum = @special.map({ $_ ** 2 }).sum;
$explain ~= "\nHence, the sum of the squares of all special "
~ "elements of given array:\n"
~ @special.map({ "$_ * $_" }).join(' + ')
~ " = " ~ $sum;
return (
$sum,
$explain
);
}
View the entire Raku script for this task on GitHub.
Perl
Nothing really changes in the Perl implementation except the normal syntax changes from Raku to Perl…
use List::Util qw( sum );
sub specialElementIndices($n) {
return grep { $n % $_ == 0 } 1 .. $n;
}
sub english_list ( @list ) {
# given a list, join it in a way that makes sense
# to english speakers
my $last = pop @list; # last element in array
if (@list == 0) {
# using an array in a scalar context returns
# the number of elements in the array
# there was only one element in the list
return $last;
}
my $joined = join qq{,\n}, @list;
if (@list > 1) {
# if there's more than element, add an Oxford comma
$joined .= q{,};
}
return "$joined and\n$last";
}
sub specialNumberSquareSum(@ints) {
# in scalar context, an array evaluates to the number
# of elements in the array
my $n = @ints;
# find the list of indices for "special" numbers
my @specialIndices = specialElementIndices($n);
my $count = @specialIndices;
my @explain_list = map {
"\$ints[$_] since $_ divides $n"
} @specialIndices;
my $explain = "There are exactly $count special elements "
. "in the given array:\n" . english_list(@explain_list);
# find the special numbers themselves
my @special = map { $ints[$_ - 1] } @specialIndices;
# find the sum of the squares
my $sum = sum( map { $_ ** 2 } @special);
$explain .= "\nHence, the sum of the squares of all special "
. "elements of given array:\n"
. join(' + ', map { "$_ * $_" } @special)
. " = " . $sum;
return (
$sum,
$explain
);
}
View the entire Perl script for this task on GitHub.
Python
Mostly what tripped me up going from Raku to Python was the lack of sigils meaning I couldn’t give variables the same names as built-in functions.
def specialElementIndices(n):
return list( filter(lambda x: n % x == 0, range(1, n+1)) )
def english_list (strlist):
# given a list, join it in a way that makes sense
# to english speakers
last = strlist.pop(-1) # last element in array
if (len(strlist) == 0):
# using an array in a scalar context returns
# the number of elements in the array
# there was only one element in the list
return last
joined = ',\n'.join(strlist)
if (len(strlist) > 1):
# if there's more than element, add an Oxford comma
joined += ','
return f'{joined} and\n{last}'
def specialNumberSquareSum(ints):
n = len(ints)
# find the list of indices for "special" numbers
specialIndices = specialElementIndices(n)
count = len(specialIndices)
explain_list = [
f"$ints[{x}] since {x} divides {n}"
for x in specialIndices
]
explain = (
"There are exactly $count special elements " +
"in the given array:\n" + english_list(explain_list)
)
# find the special numbers themselves
special = [ ints[x - 1] for x in specialIndices ]
# find the sum of the squares
sumval = sum([ x ** 2 for x in special ])
explain += '\nHence, the sum of the squares of all special '
explain += 'elements of given array:\n'
explain += ' + '.join(map(lambda x: f'{x} * {x}', special))
explain += f' = {sumval}'
return (
sumval,
explain
)
View the entire Python script for this task on GitHub.
Task 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)
Approach
This one is a little more challenging. Looking at the examples, I can see that when $n is 1, the array has to be ( 0 ). When $n is 3, the array needs to be ( -x, 0, x ) where x is some positive integer. We can extrapolate that when $n is 2, array needs to be ( -x, x ) where x is some positive integer. As $n gets larger, the number of possible arrays gets larger: the example output arrays have the form ( -(x+y), -z, z, x, y ) and ( -x, -y, z, -z, x+y ).
Rather than generate permutations of $n unique integers and check them to see if they sum to 0, I think I’m going to take the approach of generating a list I know will sum to 0 ahead of time:
- If
$nis odd, push 0 onto the list and decrement$nby one - set
$xto 1 - if
$nis still greater than 0, push-$xand$xonto the list, then decrement$nby two and increment$xby one - repeat the last step until
$nis equal to 0
With this algorithm, my output winds up being:
Example 1: Input: $n = 5 Output: (-2, -1, 0, 1, 2) Example 2: Input: $n = 3 Output: (-1, 0, 1) Example 3: Input: $n = 1 Output: (0)
But for $n = 2, the output would be (-1, 1), and for $n = 4, the output would be(-2, -1, 1, 2).
Raku
Because subroutine parameters are read-only (and you can mark them read-write with is rw only if what’s being passed into them is a writable object, not a numeric constant, I’m copying the value from the parameter to a working variable.
sub uniqueSumZero($input) {
my $n = $input;
my @list;
my $x = 1;
while ($n > 0) {
if ($n % 2 == 1) { # $n is odd
@list.push(0);
$n -= 1;
}
else { # $n is even
@list.append($x * -1, $x);
$x += 1;
$n -= 2;
}
}
return @list.sort;
}
View the entire Raku script for this task on GitHub.
Perl
Perl, however, doesn’t have the problem with the subroutine parameter being read-only, so I can modify $n to my heart’s content.
sub uniqueSumZero($n) {
my @list;
my $x = 1;
while ($n > 0) {
if ($n % 2 == 1) { # $n is odd
push @list, 0;
$n--;
}
else { # $n is even
push @list, $x * -1, $x;
$x++;
$n -= 2;
}
}
return sort { $a <=> $b } @list;
}
View the entire Perl script for this task on GitHub.
Python
def uniqueSumZero(n):
zero_sum_list = []
x = 1
while n > 0:
if (n % 2 == 1): # n is odd
zero_sum_list.append(0)
n -= 1
else: # n is even
zero_sum_list.append(x * -1)
zero_sum_list.append(x)
x += 1
n -= 2
zero_sum_list.sort()
return zero_sum_list
View the entire Python script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-252/packy-anderson