Optimizing Pandigital Multiples in Python

In this follow-up post, we’ll transform our straightforward brute-force solution into a lean, mean, Pythonic machine, avoiding strings, pruning early, and utilizing bitmasks and vectorized data structures for clarity and speed.
1. Integer‑Only Concatenation
Instead of building a string, we’ll assemble the concatenated product as an integer. We keep track of:
value: the running concatenation of each product.- **
length**How many digits have we added so far?
def concatenated_value(k):
value, length, n = 0, 0, 1
while length < 9:
prod = k * n
# fast digit count (1 ≤ prod < 100000)
if prod < 10: d = 1
elif prod < 100: d = 2
elif prod < 1000: d = 3
elif prod < 10000:d = 4
else: d = 5
# append prod by shifting and adding
value = value * (10**d) + prod
length += d
n += 1
if length > 9:
return None, None
return value, n-1 # returns (concat, n)
- Why? Eliminating string conversion removes allocation and parsing overhead.
- Early exit With
length > 9avoids wasted work.
2. Bitmask‑Based Digit Check
We can test pandigitality by building a 10‑bit mask (bits 1–9). If a digit repeats or is zero, we bail immediately.
def is_pandigital_mask(x):
mask = 0
for _ in range(9):
d = x % 10
x //= 10
bit = 1 << d
# reject zero or repeated
if d == 0 or (mask & bit):
return False
mask |= bit
return mask == 0b1111111110 # bits 1–9 set
- Uses integer arithmetic only.
- No sorting or set allocations.
3. Pruning the Search Space
Observe:
- Any base
kWith five digits or more is invalid: evenk*1||k*2exceeds nine digits. - For
kwith 1–3 digits,nmay need to be 2 or 3 to reach exactly 9 digits; forkwith 4 digits, onlyn=2works.
def valid_ks():
# 1- to 4-digit k only
for k in range(1, 10000):
yield k
More precisely, you can group by the digit length of k and precompute the required n range, but often a simple 1..9999 loop with early exits is fast enough.
4. Putting It All Together
max_val, best_k, best_n = 0, 0, 0
for k in valid_ks():
concat, n = concatenated_value(k)
if concat is None:
continue
if is_pandigital_mask(concat):
if concat > max_val:
max_val, best_k, best_n = concat, k, n
print(f"Max pandigital: {max_val} (k={best_k}, n={best_n})")
This version runs in under a millisecond in CPython and maintains crystal‑clear logic:
- Build the integer concatenation, exit early on overflow.
- Mask‑check digits quickly, exit early on failure.
- Compare and record the maximum.
5. Advanced Pythonic Touches
functools.lru_cachecan memoize digit counts or masks if reused.itertools.takewhileCan replace thewhile length<9loop for elegance.- NumPy vectorization (for extensive ranges) can offload candidate assembly to fast C loops.
Prev: Programmatically Finding Pandigital Multiples
Next: Solving Pandigital Multiples with C# and Length‑Based Pruning
Optimizing Pandigital Multiples in Python