Problem 43: Sub-string Divisibility
View on Project EulerProject Euler Problem 43 Solution
EulerSolve provides an optimized solution for Project Euler Problem 43, Sub-string Divisibility, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary We seek 10-digit pandigital numbers \(d_1d_2\cdots d_{10}\) that use each digit from 0 through 9 exactly once, with \(d_1 \neq 0\). The special condition is imposed on the seven overlapping 3-digit windows beginning at positions 2 through 8. If we define $w_i = 100d_{i+1} + 10d_{i+2} + d_{i+3}\qquad (1 \le i \le 7)$ and $(p_1,p_2,\dots,p_7) = (2,3,5,7,11,13,17),$ then the required property is $w_i \equiv 0 \pmod{p_i}\qquad \text{for every } i=1,2,\dots,7.$ The task is to sum all pandigital numbers satisfying these seven congruences simultaneously. Mathematical Approach The problem is a finite constraint filter on the set of pandigital arrangements. The useful mathematics is not a recurrence or a generating function, but the structure created by the seven sliding windows and the divisibility rules attached to them. The Seven Sliding Windows The windows are $d_2d_3d_4,\ d_3d_4d_5,\ d_4d_5d_6,\ d_5d_6d_7,\ d_6d_7d_8,\ d_7d_8d_9,\ d_8d_9d_{10}.$ They overlap by two digits each time, so a choice made in the middle of the number affects several tests at once. For example, \(d_6\) appears in the windows checked against 5, 7, and 11, while \(d_8\) appears in the windows checked against 11, 13, and 17. That overlap is the central mathematical object in the problem: the divisibility conditions are local, but they are chained together....
Detailed mathematical approach
Problem Summary
We seek 10-digit pandigital numbers \(d_1d_2\cdots d_{10}\) that use each digit from 0 through 9 exactly once, with \(d_1 \neq 0\). The special condition is imposed on the seven overlapping 3-digit windows beginning at positions 2 through 8.
If we define
$w_i = 100d_{i+1} + 10d_{i+2} + d_{i+3}\qquad (1 \le i \le 7)$
and
$(p_1,p_2,\dots,p_7) = (2,3,5,7,11,13,17),$
then the required property is
$w_i \equiv 0 \pmod{p_i}\qquad \text{for every } i=1,2,\dots,7.$
The task is to sum all pandigital numbers satisfying these seven congruences simultaneously.
Mathematical Approach
The problem is a finite constraint filter on the set of pandigital arrangements. The useful mathematics is not a recurrence or a generating function, but the structure created by the seven sliding windows and the divisibility rules attached to them.
The Seven Sliding Windows
The windows are
$d_2d_3d_4,\ d_3d_4d_5,\ d_4d_5d_6,\ d_5d_6d_7,\ d_6d_7d_8,\ d_7d_8d_9,\ d_8d_9d_{10}.$
They overlap by two digits each time, so a choice made in the middle of the number affects several tests at once. For example, \(d_6\) appears in the windows checked against 5, 7, and 11, while \(d_8\) appears in the windows checked against 11, 13, and 17. That overlap is the central mathematical object in the problem: the divisibility conditions are local, but they are chained together.
Immediate Consequences of the Divisibility Rules
Several restrictions can be read off before examining any full candidate. Since \(w_1=d_2d_3d_4\) is divisible by 2, the last digit of that window must be even, so \(d_4\) is even. Since \(w_3=d_4d_5d_6\) is divisible by 5, its last digit must be 0 or 5, so
$d_6 \in \{0,5\}.$
Since \(w_2=d_3d_4d_5\) is divisible by 3, its digit sum must be divisible by 3, hence
$d_3 + d_4 + d_5 \equiv 0 \pmod{3}.$
These are not separate side facts. Because the windows overlap, a decision forced by one prime immediately feeds into later primes. This is why valid pandigital numbers are very rare even though the raw search space is only \(10!\).
Worked Example: \(1406357289\)
The checkpoint example used by the implementations is
$1406357289.$
Its seven windows are
$406,\ 063,\ 635,\ 357,\ 572,\ 728,\ 289.$
Now check them against the prime list in order:
$406 \equiv 0 \pmod{2},\quad 63 \equiv 0 \pmod{3},\quad 635 \equiv 0 \pmod{5},$
$357 \equiv 0 \pmod{7},\quad 572 \equiv 0 \pmod{11},\quad 728 \equiv 0 \pmod{13},\quad 289 \equiv 0 \pmod{17}.$
The block \(063\) is perfectly valid here: it is the value of the digits in positions 3 through 5, and numerically it equals 63. This example shows exactly what the predicate tests and why the windows must be interpreted positionally rather than as standalone decimal strings.
Why Exhaustive Enumeration Is Enough
No recurrence is needed because every candidate can be checked independently. There are exactly
$10! = 3{,}628{,}800$
pandigital arrangements of the ten digits. The implementation simply evaluates the seven-window predicate on each arrangement, rejecting those with leading zero because they are not 10-digit numbers. Even in the worst case this means only \(7 \cdot 10!\) modular checks, which is a modest amount of work for modern hardware.
How the Code Works
The C++, Python, and Java implementations all follow the same high-level algorithm: enumerate every arrangement of the digits 0 through 9, test the substring divisibility property, and add the numeric value of each valid arrangement to a running total.
Enumerating Candidates
The compiled implementations begin from the ordered digit sequence 0123456789 and move through the arrangements in lexicographic order. The Python implementation asks the standard permutation iterator for all arrangements of the same ten digits. In every case, uniqueness of digits is automatic because the generator itself produces permutations.
Testing the Predicate
For each candidate, the implementation first checks whether the first digit is zero. If so, the candidate is rejected immediately. Otherwise it builds the seven window values \(w_1,\dots,w_7\) directly from adjacent digits and compares them with the fixed prime list \((2,3,5,7,11,13,17)\). The candidate is accepted only if all seven congruences hold.
Accumulating the Sum and Verifying Correctness
Whenever a candidate passes the predicate, the full 10-digit value is converted to an integer and added to the running total. Before the complete sweep begins, the implementations also perform small sanity checks: the known valid example \(1406357289\) must pass, while an obvious non-example such as \(1234567890\) must fail. Those checkpoints ensure that the divisibility test is wired to the correct positions and primes.
Complexity Analysis
The search visits all \(10!\) permutations, so the running time is \(O(10!)\). More concretely, it examines 3,628,800 candidates, and each candidate requires only constant work: one leading-zero test, seven 3-digit constructions, and seven modular reductions in the worst case.
The extra memory usage is \(O(1)\) beyond the current candidate and the running total. The C++ and Java versions mutate a small in-memory digit container in place, while the Python version receives candidates lazily from the permutation iterator, so none of the implementations stores the full search space.
Footnotes and References
- Problem page: Project Euler 43 - Sub-string Divisibility
- Pandigital numbers: Wikipedia - Pandigital number
- Divisibility rules: Wikipedia - Divisibility rule
- Permutations: Wikipedia - Permutation
- Lexicographic order: Wikipedia - Lexicographic order