Teaser 3316: The odd one out
From The Sunday Times, 12th April 2026 [link]
George drew a 3 × 3 grid and wrote an odd digit in each square. Martha did the same and her grid was identical to George’s except for the digit in one square. Both grids contained eight different three-digit prime numbers, reading across the three rows, down the three columns and down the two diagonals. Remarkably, all eight of George’s primes were also primes when read backwards but only seven of Martha’s primes had this property.
Which prime number in Martha’s grid was the odd one out?
[teaser3316]














Jim Randell 7:33 am on 12 April 2026 Permalink |
Here is a declarative solution using the [[
SubstitutedExpression]] solver from the enigma.py library.It runs in 134ms. (Internal runtime of the generated code is 41ms).
Run: [@codepad]
Solution: [To Be Revealed]
LikeLike
Jim Randell 2:51 pm on 12 April 2026 Permalink |
Splitting out the condition that George and Martha’s grids differ in exactly one position, into multiple expressions, allows us to get a run-file with an internal run time of just 1.8ms.
The run time can be further reduced (to 1.3ms) with the observation that 5 cannot occur at the end of a prime, or at the beginning of a bi-directional prime.
LikeLike
Ruud 8:31 am on 12 April 2026 Permalink |
import istr primes13579 = {i for i in istr.primes(100, 1000) if all(c in [1, 3, 5, 7, 9] for c in i)} reversible_primes13579 = {i for i in primes13579 if i.reversed().is_prime()} def grid(number_reversible): for rows in istr.permutations(primes13579, 3): all8 = list(rows) for i in range(3): all8.append(rows[0][i] | rows[1][i] | rows[2][i]) all8.append(rows[0][0] | rows[1][1] | rows[2][2]) all8.append(rows[0][2] | rows[1][1] | rows[2][0]) if len(set(all8)) == 8: # all different? if all(n in primes13579 for n in all8) and sum(n in reversible_primes13579 for n in all8) == number_reversible: yield all8 georges = list(grid(number_reversible=8)) for martha in grid(number_reversible=7): for george in georges: diff = sum(g != m for g, m in zip(george[0] | george[1] | george[2], martha[0] | martha[1] | martha[2])) if diff == 1: for p in martha: if p not in reversible_primes13579: print(p) for g, m in zip(george[:3], martha[:3]): print(" ", g, m) print()LikeLiked by 1 person
GeoffR 5:20 pm on 12 April 2026 Permalink |
# ST 3316 by Claude AI from itertools import product sieve = bytearray([1]) * 1000 sieve[0] = sieve[1] = 0 for i in range(2, 32): if sieve[i]: sieve[i*i::i] = bytes(len(sieve[i*i::i])) ODD = (1,3,5,7,9) P = {t: sieve[100*t[0]+10*t[1]+t[2]] for t in product(ODD, repeat=3)} R = {t: sieve[100*t[2]+10*t[1]+t[0]] for t in product(ODD, repeat=3)} E = {t: P[t] and R[t] for t in product(ODD, repeat=3)} def search(check): found = [] for a,b,c in product(ODD, repeat=3): if not check[(a,b,c)]: continue for d,e,f in product(ODD, repeat=3): if not check[(d,e,f)]: continue for g,h,i in product(ODD, repeat=3): if (check[(g,h,i)] and check[(a,d,g)] and check[(b,e,h)] and check[(c,f,i)] and check[(a,e,i)] and check[(c,e,g)]): nums = {100*a+10*b+c, 100*d+10*e+f, 100*g+10*h+i, 100*a+10*d+g, 100*b+10*e+h, 100*c+10*f+i, 100*a+10*e+i, 100*c+10*e+g} if len(nums) == 8: found.append((a,b,c,d,e,f,g,h,i)) return found george = search(E) seen = set() for gg in george: for pos in range(9): for digit in ODD: if digit == gg[pos]: continue mg = list(gg); mg[pos] = digit a,b,c,d,e,f,g,h,i = mg tris = [(a,b,c),(d,e,f),(g,h,i),(a,d,g),(b,e,h),(c,f,i),(a,e,i),(c,e,g)] if not all(P[t] for t in tris): continue nums = [100*t[0]+10*t[1]+t[2] for t in tris] if len(set(nums)) != 8: continue flags = [R[t] for t in tris] if sum(flags) == 7: ans = nums[flags.index(False)] if ans not in seen: seen.add(ans) print(f"Grid: {mg[:3]} / {mg[3:6]} / {mg[6:]}") print(f" Odd prime out: {ans} (reversal {int(str(ans)[::-1])} is not prime)") print(f"\nAnswer: {sorted(seen)}")LikeLike
Jim Randell 6:27 pm on 12 April 2026 Permalink |
Claude doesn’t seem to believe that comments are useful in code.
LikeLike
GeoffR 6:34 pm on 12 April 2026 Permalink |
It depends on the prompt – I asked Claude AI to make the code short and fast, and it chose to leave the comments out.
LikeLike
Brian Gladman 3:23 pm on 13 April 2026 Permalink |
Perhaps you shouldn’t encourage its bad habits!
LikeLike
Frits 7:01 pm on 12 April 2026 Permalink |
Not considering reflections/rotations.
from itertools import permutations # 3-digit primes with odd digits P = [3, 5, 7] P += [x for x in range(11, 33, 2) if all(x % p for p in P)] P = {s for x in range(101, 1000, 2) if all(x % p for p in P) and set("13579").issuperset(s := str(x))} # reversable primes for George G = {p for p in P if p[::-1] in P} # return sequence of 3 rows, 3 columns and 2 diagonals def nums8(rs): seq = set(''.join(r) for r in rs) # add columns seq.update(list(''.join(c) for c in zip(*[list(r) for r in rs]))) # add diagonals seq.update((rs[0][0] + rs[1][1] + rs[2][2], rs[0][2] + rs[1][1] + rs[2][0])) return seq sols = set() # select reversable primes for George for rs in permutations(G, 3): rowsG = [list(r) for r in rs] # check the two diagonals for George d1, d2 = rs[0][0] + rs[1][1] + rs[2][2], rs[0][2] + rs[1][1] + rs[2][0] if any(x not in G for x in (d1, d2)): continue # check the columns for George colsG = list(''.join(c) for c in zip(*rowsG)) if any(c not in G for c in colsG): continue # check for 8 different numbers for George if len(nums8(rowsG)) != 8: continue # change one digit in George's grid in cell [r][c] for r in range(3): for c in range(3): rowsM = list(row.copy() for row in rowsG) # choose new digit value for v in "13579": if v == rowsG[r][c]: continue rowsM[r][c] = v # check for 8 different numbers for Martha if len(numsM := nums8(rowsM)) != 8: continue # all Martha's 8 numbers must also be prime if any(n not in P for n in numsM): continue # count number of reversed numbers not being prime np = [x for x in numsM if x[::-1] not in P] if len(np) != 1: continue # seven were prime sols.add(np[0]) print(f"answer: {' or '.join(sols)}")LikeLike