Project Euler Problem 954
A positive integer is called heptaphobic if it is not divisible by seven and no number divisible by seven can be produce
Solution
Answer: 736463823
Let a number $n$ have digits $d_i$ at decimal positions $i$ (least significant digit = position $0$). Suppose we swap digits $d_i=a$ and $d_j=b$. The new number becomes
$$n' = n + (b-a)(10^i - 10^j).$$
We want $n'$ to never be divisible by $7$, while $n$ itself is also not divisible by $7$.
Working modulo $7$, since $10^k \bmod 7$ is periodic with period $6$,
$$10^k \equiv (1,3,2,6,4,5,\ldots)\pmod 7.$$
For a fixed target remainder $r=n\bmod 7\neq 0$, a swap between positions with residues $x=10^i\bmod 7$ and $y=10^j\bmod 7$ is forbidden iff
$$r + (b-a)(x-y)\equiv 0 \pmod 7,$$
equivalently
$$b \equiv a - r(y-x)^{-1}\pmod 7.$$
Thus, for each final remainder $r\in{1,\dots,6}$, we can build a digit-DP:
- process digits left to right,
- track current remainder mod $7$,
- track which digit residues mod $7$ have already appeared in each of the six position residue classes,
- reject any digit that would create a swap yielding divisibility by $7$,
- enforce the “no leading zero after swap” rule.
This DP reproduces the checks:
- $C(100)=74$
- $C(10^4)=3737$
as required.
Clean Python implementation:
from functools import lru_cache
mods = [pow(10, i, 7) for i in range(13)]
def count_length(L):
posmods = list(reversed(mods[:L])) # MSD -> LSD
total = 0
for r in range(1, 7):
# forbidden digit residues lookup
block = [[None] * 7 for _ in range(7)]
for x in range(1, 7):
for y in range(1, 7):
arr = [0] * 128
diff = (y - x) % 7
single = [0] * 7
if diff:
inv = pow(diff, -1, 7)
k = (-r * inv) % 7
for m in range(7):
single[m] = 1 << ((m + k) % 7)
for mask in range(128):
b = 0
mm = mask
while mm:
lsb = mm & -mm
m = lsb.bit_length() - 1
b |= single[m]
mm ^= lsb
arr[mask] = b
block[x][y] = arr
x0 = posmods[0]
@lru_cache(None)
def dp(i, rem, masks, msd_res):
if i == L:
return rem == r
x = posmods[i]
forbidden = 0
tmp = masks
for y in range(1, 7):
forbidden |= block[x][y][tmp & 127]
tmp >>= 7
msd_forbidden = 0
if i > 0 and (x0 - x) % 7:
diff = (x0 - x) % 7
inv = pow(diff, -1, 7)
k = (-r * inv) % 7
msd_forbidden = 1 << ((msd_res + k) % 7)
ans = 0
for d in range(10):
if i == 0 and d == 0:
continue
f = forbidden
if i > 0 and d != 0:
f |= msd_forbidden
dr = d % 7
if (f >> dr) & 1:
continue
nmasks = masks
if i > 0:
shift = (x - 1) * 7
nmasks |= (1 << dr) << shift
ans += dp(i + 1, (rem * 10 + d) % 7,
nmasks, msd_res)
return ans
for first_digit in range(1, 10):
total += dp(1, first_digit % 7, 0,
first_digit % 7)
return total
answer = sum(count_length(L) for L in range(1, 14))
print(answer)
The computation gives:
$$C(10^{13}) = 736463823.$$
Answer: 736463823