sub valid_set { my($p, $known) = @_; my(%seenzero, %seenmore); for my $index (0 .. $#$known) { # look only at the defined values my $power = $known->[$index] // next; if ($power > 0) { # we've seen a power > 0, record its value modulo p ++$seenmore{$index % $p}; } else { # we've seen a power = 0, record its value modulo p ++$seenzero{$index % $p}; } } # the values _not_ divisible by p can be distributed across at most # p - 1 distinct values modulo p return 0 if keys(%seenzero) >= $p; # [update] bugfix: the values that _are_ divisible by p must not have # the same value modulo p as those that are not. Thanks LanX. :) return 0 if grep $seenzero[$_], keys %seenmore; # [end update] # the values that _are_ divisible by p must all have the same value # modulo p return 0 if keys(%seenmore) > 1; my($mod, $seen) = each %seenmore; # if there were no values divisible by p, or exactly one such, we're done return 1 if ($seen // 0) <= 1; # else reduce the list to every p'th element, and recurse my @higher; for (my $i = $mod; $i < @$known; $i += $p) { push @higher, defined($known->[$i]) ? $known->[$i] - 1 : undef; } return valid_set($p, \@higher); } # test the examples: expect [yes, yes, no, no] print valid_set(@$_) ? "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]], );