Problem 571: Super Pandigital Numbers
View on Project EulerProject Euler Problem 571 Solution
EulerSolve provides an optimized solution for Project Euler Problem 571, Super Pandigital Numbers, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary Call a positive integer \(b\)-pandigital if its base-\(b\) representation contains every digit \(0,1,\dots,b-1\) at least once. It is \(n\)-super-pandigital if this happens for every base \(b\) with \(2 \le b \le n\). The task is to find the 10 smallest 12-super-pandigital numbers and sum them. The key point is that the smallest solutions cannot come from arbitrary integers. Any 12-super-pandigital number must already be pandigital in base 12, and that forces a very rigid structure that makes an ordered search possible. Mathematical Approach The method used by the implementations rests on five ideas: restrict the search to the smallest possible base-12 length, test digit coverage with a bit mask, reject candidates as soon as one base fails, preserve numerical order through lexicographic permutation order, and jump directly to permutation blocks with the factorial number system. Step 1: Restrict the Search to the Minimal Base-12 Layer If \(x\) is 12-super-pandigital, then its base-12 expansion must contain every digit \(0,1,\dots,11\). Therefore the expansion must have at least 12 digits. The smallest possible candidates are exactly those with 12 digits in base 12 ....
Detailed mathematical approach
Problem Summary
Call a positive integer \(b\)-pandigital if its base-\(b\) representation contains every digit \(0,1,\dots,b-1\) at least once. It is \(n\)-super-pandigital if this happens for every base \(b\) with \(2 \le b \le n\). The task is to find the 10 smallest 12-super-pandigital numbers and sum them.
The key point is that the smallest solutions cannot come from arbitrary integers. Any 12-super-pandigital number must already be pandigital in base 12, and that forces a very rigid structure that makes an ordered search possible.
Mathematical Approach
The method used by the implementations rests on five ideas: restrict the search to the smallest possible base-12 length, test digit coverage with a bit mask, reject candidates as soon as one base fails, preserve numerical order through lexicographic permutation order, and jump directly to permutation blocks with the factorial number system.
Step 1: Restrict the Search to the Minimal Base-12 Layer
If \(x\) is 12-super-pandigital, then its base-12 expansion must contain every digit \(0,1,\dots,11\). Therefore the expansion must have at least 12 digits.
The smallest possible candidates are exactly those with 12 digits in base 12. In that case every required digit appears once, so the digit string is a permutation of
$\{0,1,2,\dots,11\}.$
The leading digit cannot be \(0\), because that would really be an 11-digit representation. Hence the minimal search layer consists of all base-12 numbers whose digits are a permutation of \(0,1,\dots,11\) with nonzero first digit.
The number of such candidates is
$12!-11!=11\cdot 11!.$
This is the whole reason the search is feasible: once the program finds 10 valid numbers inside this 12-digit layer, they are automatically the 10 smallest overall, because every base-12 number with more than 12 digits is larger than every member of the minimal layer.
Step 2: Test Pandigitality in One Base with a Coverage Mask
Fix a base \(b\). Repeated division writes \(x\) as
$x=d_0+d_1b+d_2b^2+\cdots,\qquad 0\le d_i<b.$
Every remainder \(d_i\) is a digit of the base-\(b\) expansion. To record which digits appear, set bit \(d_i\) in a mask. In mathematical notation, define
$S_b(x)=\bigvee_i 2^{d_i},$
where \(\vee\) denotes bitwise OR. The full mask for base \(b\) is
$M_b=2^b-1.$
Then
$x\text{ is }b\text{-pandigital}\iff S_b(x)=M_b.$
This criterion is exact because digit \(j\) appears at least once if and only if bit \(j\) is set. It also supports early exit: once the mask has become \(M_b\), the remaining digits no longer matter.
Step 3: Turn Super-Pandigitality into a Short-Circuit Test
Every candidate in the minimal layer is already 12-pandigital by construction, so only the smaller bases remain to be checked. Thus
$x\text{ is 12-super-pandigital}\iff \forall b\in\{2,3,\dots,11\},\ S_b(x)=2^b-1.$
The implementations test these bases from larger to smaller and stop immediately at the first failure. This is mathematically sound because the condition is a conjunction: one failed base is enough to reject the candidate.
Step 4: Enumerate Candidates in Increasing Numerical Order
Every minimal candidate has the form
$x=a_{11}12^{11}+a_{10}12^{10}+\cdots+a_1 12+a_0,$
where \((a_{11},a_{10},\dots,a_0)\) is a permutation of \(0,1,\dots,11\) and \(a_{11}\ne 0\).
For fixed length and fixed base, lexicographic order of the digit tuples is the same as numerical order. The first differing higher-position digit dominates all lower positions. Therefore a lexicographic permutation scan lists these candidates from smallest to largest.
That justifies two practical rules:
$\text{skip every permutation whose first digit is }0,$
and
$\text{stop as soon as 10 hits have been collected.}$
Step 5: Use the Factorial Number System to Jump to Blocks
The optimized 12-case search does not restart from the beginning for every worker. Instead it uses factoradic indexing, which maps an integer \(k\) directly to the \(k\)-th permutation in lexicographic order.
Because the first \(11!\) lexicographic permutations begin with digit \(0\), the useful interval is
$11!\le k<12!.$
This makes block decomposition straightforward. The C++ implementation splits the interval into chunks of size
$9!,$
lets each worker decode the first permutation of its block, and then advances lexicographically within that block. The blocks are disjoint, so parallelism changes only running time, not the mathematics.
Worked Example: Why \(978\) is 5-Super-Pandigital
A useful small checkpoint is the smallest 5-super-pandigital number, \(978\). Its representations are
$978=12403_5=33102_4=1100020_3=1111010010_2.$
In base \(5\), the digits \(0,1,2,3,4\) all appear. In base \(4\), the digits \(0,1,2,3\) all appear. In base \(3\), the digits \(0,1,2\) all appear. In base \(2\), both digits \(0\) and \(1\) appear. Hence \(978\) is pandigital in every base from \(2\) through \(5\), so it is 5-super-pandigital.
How the Code Works
The C++, Python, and Java implementations all follow the same mathematical plan. They search the minimal base-12 pandigital layer, convert each permutation into its numeric value, and then test bases \(11\) down to \(2\) with the coverage-mask criterion.
The generic per-base test repeatedly extracts a remainder, sets the corresponding bit, divides by the base, and compares the current mask against the full mask. The C++ implementation adds two engineering optimizations for the final 12-case. For base \(8\), digit extraction becomes
$x\bmod 8=x\,\&\,7,\qquad \left\lfloor\frac{x}{8}\right\rfloor=x\gg 3,$
so the loop uses fast bit operations. For the remaining search space, factoradic block starts allow multiple workers to scan disjoint chunks in parallel before the hits are merged and sorted.
The Java implementation uses the same mathematical search but performs the permutation scan sequentially through direct index-to-permutation decoding. The Python implementation delegates execution to the compiled search, so it inherits the same ordering of candidates and the same pandigital tests.
Complexity Analysis
The minimal layer contains \(12!-11!\) candidates. For each candidate, at most 10 additional bases need to be checked. A single base check takes time proportional to the number of digits of the candidate in that base, which is bounded by a small constant because \(n=12\) is fixed.
So, for this problem, the worst-case running time is dominated by scanning the permutation layer and is effectively \(O(12!)\), with strong constant-factor reductions from early rejection, base-specific fast paths, and parallel block processing. Memory usage is \(O(1)\) per worker plus a small buffer for the successful hits.
Footnotes and References
- Problem page: https://projecteuler.net/problem=571
- Pandigital number: Wikipedia — Pandigital number
- Positional notation: Wikipedia — Positional notation
- Factorial number system: Wikipedia — Factorial number system
- Lexicographic order: Wikipedia — Lexicographic order
- Bitwise operation: Wikipedia — Bitwise operation