here my take on it:
The basic idea is that the maximal known value in a sequence rules it's neighborhood.
I probably over optimized it: I thought that comparing two arrays should be fastest and I'm caching those static sieves for comparison.
Of course it's also possible to work without a sieve-table and calculate on the entries on the fly. (So maybe premature optimization on my part)
This must be benchmarked and depends a lot on processor's caching of perl data structures.
Please test and tell me if you find a bug.
FWIW: I left a lot of debug code inside which is optimized away based on the constant DBG
# https://perlmonks.org/?node_id=11142871
use v5.12;
use warnings;
use Data::Dump qw/pp dd/;
my $range = 32;
my %sieves;
use constant DBG => 0;
sub get_sieve {
my ( $prime, $range ) = @_;
my @sieve = 0; # p^0 = 1
my @pos = (0);
while ( @sieve < 2*$range ) {
@sieve = (@sieve) x $prime ;
$sieve[-1]++;
push @pos, @sieve-1;
}
pop @pos; # forget edge
return {
prime => $prime,
sieve => [ @sieve[0 .. 2*$range-1] ],
sieve_pos => \@pos,
sieve_max => @pos-1,
};
}
sub valid_set_lanx {
warn "call:",pp \@_ if DBG;
my($p, $a_known) = @_;
my $h_sieve_cache =
$sieves{$p} //= get_sieve($p,$range);
my ($a_sieve, $a_sieve_pos, $sieve_max) = @{$h_sieve_cache}{qw/sie
+ve sieve_pos sieve_max/};
warn pp $h_sieve_cache if DBG;
# --- find max_known
my @known = @$a_known;
my $max_known = 0;
my $max_pos = 0;
my $idx = 0;
for my $e (@known) {
next unless defined $e;
if ($e > $max_known) {
$max_known = $e;
$max_pos = $idx;
}
} continue {
$idx++;
}
# --- adjust into sieve range
if ($max_known > $sieve_max) {
$max_known = $sieve_max;
$known[$max_pos] = $max_known;
}
my $idx_sieve = $a_sieve_pos->[$max_known]- $max_pos;
if ( DBG ) {
#warn pp [\@known,$a_sieve,$max_known,$a_sieve_pos->[$max_know
+n],$idx_sieve,$max_pos];
warn (". "x $idx_sieve, join " ", map { $_ // "?" } @known);
warn "@$a_sieve";
}
my $ok = 1;
for my $e (@known) {
#warn (($a_sieve->[$idx_sieve] //"?"), " - ", ($e//"?")) if DB
+G;
next unless defined $e;
$ok=0 if $e != $a_sieve->[$idx_sieve]
} continue {
$idx_sieve++
}
return $ok;
}
# test the examples: expect [yes, yes, no, no]
print valid_set_lanx(@$_) ? "yes\n" : "no\n"
for (
[ 2, [0, 1, 0, 2, 0]],
[ 2, [1, 0, 5, 0, 1]],
[ 2, [2, undef, undef, undef, 2]],
[ 3, [1, 0, 0, 1, 0, 0, 1]],
[ 2, [1, 0, 8, 0, 1]],
);
OUTPUT: yes
yes
no
no
yes
update
holes are tricky, still need to cover this buggy edge case, which is legit but won't pass now.
[ 3, [0, undef, 0, 0, 1]]
|