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
$n
is odd, push 0 onto the list and decrement$n
by one - set
$x
to 1 - if
$n
is still greater than 0, push-$x
and$x
onto the list, then decrement$n
by two and increment$x
by one - repeat the last step until
$n
is 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