ETOOBUSY 🚀 minimal blogging for the impatient
Autobiographical numbers constraints - basic
TL;DR
Remember last post about Autobiographical numbers? Here we will take a look at the constraints - horribly laid out, but effective (although maybe not very efficient).
So… the constraints. I coded a few of them, let’s start from a very basic one - although pretty effective on its own terms. If you want to try the code below, you can find it in autobiographical-numbers (the code itself is in stage 1).
The code
1 sub constraint_basic ($status) { # little pruning, mostly checking
2 my $solution = $status->{solution};
3 my %count_for;
4 my %is_exact = map { $_ => 1 } 0 .. $#$solution;
5 for my $i (0 .. $#$solution) {
6 my $slot = $solution->[$i];
7 my @candidates = keys $slot->%*;
8 die "no candidate for $i" unless @candidates;
9 my $exact = @candidates == 1;
10 for my $candidate (@candidates) {
11 $count_for{$candidate}++;
12 $is_exact{$candidate} = 0 unless $exact;
13 }
14 } ## end for my $i (0 .. $#$solution)
15 my $changes = 0;
16 for my $i (0 .. $#$solution) {
17 my $amount = $count_for{$i} // 0;
18 my $slot = $solution->[$i];
19 if ($is_exact{$i}) {
20 die "invalid amount if $i" unless exists $slot->{$amount};
21 if (scalar keys $slot->%* > 1) { # prune
22 $slot->%* = ($amount => 1);
23 $changes++;
24 }
25 }
26 else {
27 for my $needed (keys $slot->%*) {
28 if ($needed > $amount) {
29 delete $slot->{$needed}; # this can't be fulfilled
30 $changes++;
31 }
32 }
33 die "no valid candidate for $i" unless scalar keys $slot->%*;
34 }
35 } ## end for my $i (0 .. $#$solution)
36 return $changes;
37 } ## end sub constraint_basic ($status)
The first loop (lines 5 to 14) counts the number of available slots where a
specific amount can appear, collecting it in hash %count_for
.
Additionally, it also tracks whether the specific amount appears exactly
in all those slots, i.e. if it’s the only candidate remained in there.
The following loop (lines 16 to 35) does the constraints check and possibly
pruning, keeping track of the number of changes on the way (so that the
constraint function can signal whether pruning happened or not via
return
).
Every slot is checked (line 16); the amount associated is the count of possible slots where it might appear (line 17).
If the count associated to this slot is exact (i.e. it appeared as the only candidate in all slots where it was present), then we have to make an exact check. Hence we throw an exception in case this amount is not among the possibilities for this slot (line 20) or, if present, we make sure to mark it as the only possibility (lines 21 to 24, which avoid doing this operation over and over).
If the count is not exact, then $amount
represents the maximum number of
times that the specific value of the slot (i.e. $i
) can appear overall.
For this reason, we can rule out all possibilities that would require more
than $amount
(lines 28 to 32). After this pruning, we must have some
residual candidate for the slot, otherwise this cannot be a solution (line
33).
How does it go?
I daresay that it goes pretty well - enough to solve the puzzle for the YouTube video in a bunch of milliseconds:
$ time ./run.sh 01-basic/ 10
solution => [6,2,1,0,0,0,1,0,0,0]
real 0m0.084s
user 0m0.068s
sys 0m0.008s
So long…
Curious about the whole series? Here it is:
- Autobiographical numbers
- Autobiographical numbers constraints - basic
- Autobiographical numbers constraints - last is zero
- Autobiographical numbers constraints - weighted sum
- Autobiographical numbers constraints - luckier weighted sum
- Autobiographical numbers - step up
- Code repository
Comments? Please comment below!