ETOOBUSY 🚀 minimal blogging for the impatient
PWC172 - Prime Partition
TL;DR
Here we are with TASK #1 from The Weekly Challenge #172. Enjoy!
The challenge
You are given two positive integers,
$m
and$n
.Write a script to find out the
Prime Partition
of the given number. No duplicates allowed.For example,
Input: $m = 18, $n = 2 Output: 5, 13 or 7, 11 Input: $m = 19, $n = 3 Output: 3, 5, 11
The questions
No questions asked.
I mean, there would be so many questions that they don’t make sense. I read this challenge as OK folks, here’s something to play with, have fun and don’t annoy the neighbors!
Sooooo…
- I’ll assume that $n$ is a target positive integer that we have to partition into the sum of distinct primes;
- I’ll assume that $m$ represents the number of primes that we have to use for this partition (the examples seem to support this);
- I’ll return a list (or an array) of $m$ primes or the empty list, in case the partition is not feasible;
- I’ll assume that no optimization is necessary and that the most simple solution is OK.
The solution
The plan is to brush out the corner cases and use this:
- find all primes between $2$ and $n - 2$ and put them in an array;
- try out combinations of $m$ elements from the array:
- return the combination if its sum equals $n$;
- continue otherwise.
Raku goes first and provides batteries for most moving parts, leading to a very compact solution:
#!/usr/bin/env raku
use v6;
sub MAIN ($n = 18, $m = 2) { say prime-partition($n, $m) }
multi sub prime-partition ($n where *.is-prime, 1) { [ $n ] }
multi sub prime-partition ($n, 1) { [] }
multi sub prime-partition ($n, $m) {
for (2 .. $n - 2).grep({.is-prime}).combinations($m) -> $c {
return $c.Array if $c.sum == $n;
}
return [];
}
The multi subroutine allow us to cope with the case where $m = 1$, which boils down to checking whether $n$ is prime or not. Otherwise we apply the algorithm laid out at the beginning of this section.
On the Perl side there’s less out of the box, but with a little help here and there (e.g. from Combinations iterator) we get up to speed in little time:
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use List::Util 'sum';
my $n = shift // 18;
my $m = shift // 2;
say simple_stringify_array(prime_partition($n, $m));
sub simple_stringify_array (@a) { return '(' . join(', ', @a), ')' }
sub prime_partition ($n, $m) {
if ($m == 1) { return is_prime($n) ? $n : () }
my $cit = combinations_iterator($m, primes_within(2, $n - 2));
while (my ($c) = $cit->()) {
return $c->@* if $n == sum $c->@*;
}
return;
}
sub combinations_iterator ($k, @items) {
my @indexes = (0 .. ($k - 1));
my $n = @items;
return sub {
return unless @indexes;
my (@combination, @remaining);
my $j = 0;
for my $i (0 .. ($n - 1)) {
if ($j < $k && $i == $indexes[$j]) {
push @combination, $items[$i];
++$j;
}
else {
push @remaining, $items[$i];
}
}
for my $incc (reverse(-1, 0 .. ($k - 1))) {
if ($incc < 0) {
@indexes = (); # finished!
}
elsif ((my $v = $indexes[$incc]) < $incc - $k + $n) {
$indexes[$_] = ++$v for $incc .. ($k - 1);
last;
}
}
return (\@combination, \@remaining);
}
}
sub primes_within ($min, $max) {
my @retval = $min < 3 ? 2 : ();
$min++ unless $min % 2;
while ($min <= $max) {
push @retval, $min if is_prime($min);
$min += 2;
}
return @retval;
}
sub is_prime { # https://en.wikipedia.org/wiki/Primality_test
return if $_[0] < 2;
return 1 if $_[0] <= 3;
return unless ($_[0] % 2) && ($_[0] % 3);
for (my $i = 6 - 1; $i * $i <= $_[0]; $i += 6) {
return unless ($_[0] % $i) && ($_[0] % ($i + 2));
}
return 1;
}
Well… stay safe people!