Teaser 3265: Easter prayer
From The Sunday Times, 20th April 2025 [link] [link]
Millions of people will today turn their thoughts to those throughout the world whose lives are made miserable by the ravages of war and bitter conflict. I have taken a selection of letters from the alphabet and given each a different single-digit value. In this way the two words that form the title of this teaser represent two six-digit numbers, one of which is a factor of the other.
There are two letters in EASTER PRAYER for which the following is true. If I told you the value of that letter alone, you wouldn’t be able to work out all the others, but if I told you both their values then you could work out all the values.
What is the value of PRAYER?
[teaser3265]
Jim Randell 7:49 am on 20 April 2025 Permalink |
This Python program uses the [[
SubstitutedExpression]] solver from the enigma.py library to find solutions to the alphametic puzzle, and then looks for a pair of symbols that allow a unique solution to be determined if both their values are known, but not if only one of their values is known.It runs in 155ms. (Internal runtime is 72ms).
from enigma import (SubstitutedExpression, filter_unique, singleton, subsets, translate, printf) # find solutions to the alphametic puzzle expr = "EASTER % PRAYER == 0 or PRAYER % EASTER == 0" tmpl = "[EASTER={EASTER} PRAYER={PRAYER}]" p = SubstitutedExpression(expr, template=tmpl) ss = list(p.solve(verbose="T")) # for each symbol record values with multiple solutions rs = set() for k in p.symbols: for s in filter_unique(ss, (lambda s: s[k])).non_unique: rs.add((k, s[k])) # look for pairs that give a unique solution for ((k1, v1), (k2, v2)) in subsets(rs, size=2): if k1 == k2: continue s = singleton(s for s in ss if s[k1] == v1 and s[k2] == v2) if s is None: continue # output solution (EASTER, PRAYER) = (translate(x, s) for x in ["EASTER", "PRAYER"]) printf("{k1}={v1} {k2}={v2} -> EASTER={EASTER} PRAYER={PRAYER}")Solution: PRAYER = 159275.
There are 3 candidate solutions:
In each case EASTER = 5 × PRAYER.
And if we were told either of S = 6 or T = 3 we could only narrow the candidates down to 2 solutions. But if we are told both together, then we can narrow the candidates down to a single solution (the first one).
LikeLike
Frits 1:49 pm on 20 April 2025 Permalink |
As usual built for speed.
Always difficult to code a branch that is not easy to test.
from itertools import permutations, combinations, product from functools import reduce # digits to number d2n = lambda *s: reduce(lambda x, y: 10 * x + y, s) # return <n>th digit of number <num>, n has to be > 0 nth = lambda num, n, ln=1: int(str(num)[n - 1:n - 1 + ln]) # EASTER % PRAYER == 0 or PRAYER % EASTER == 0 cands = [] for E, R in permutations(range(10), 2): if not E: continue ER = d2n(E, R) for m in range(2, 10): # multiplier if (m * ER) % 100 != ER: continue if E >= m: # EASTER might be the numerator for P in [n for n in range(1, E // m + 1) if n not in {E, R}]: PR = d2n(P, R) # check if m * PR9999 can become at least Exxxxx if nth(m * (PR + 1), 1) < E: continue for A in set(range(10)) - {E, R, P}: PRA = d2n(P, R, A) EA = d2n(E, A) # check if m * PRA999 can become at least EAxxxx if nth(m * (PRA + 1), 1, 2) < EA: continue for Y in set(range(10)) - {E, R, P, A}: PRAYER = d2n(P, R, A, Y, E, R) EASTER = m * PRAYER if nth(EASTER, 1, 2) != EA: continue S = nth(EASTER, 3) T = nth(EASTER, 4) if S == T or not {E, R, P, A, Y}.isdisjoint({S, T}): continue cands.append(([A, E, P, R, S, T, Y])) if m * E < 10: # EASTER might be the denominator for P in range(m * E, 10): if P == R: continue PR = d2n(P, R) for A in set(range(10)) - {E, R, P}: EA = d2n(E, A) # check if m * EA9999 can become at least PRxxxx if nth(m * (EA + 1), 1, 2) < PR: continue PRA = d2n(P, R, A) for S in set(range(10)) - {E, R, P, A}: EAS = d2n(E, A, S) # check if m * EAS999 can become at least PRAxxx if nth(m * (EAS + 1), 1, 3) < PRA: continue for T in set(range(10)) - {E, R, P, A, S}: EASTER = d2n(E, A, S, T, E, R) PRAYER = m * EASTER if nth(PRAYER, 1, 3) != PRA: continue Y = nth(PRAYER, 4) if Y in {E, R, P, A, S, T}: continue cands.append(([A, E, P, R, S, T, Y])) if not cands: print("no solution") # positions of letters that have multiple values (but not all different) pos = [j for j in range(7) if 1 < len({c[j] for c in cands}) < len(cands)] # choose two of those letters for a, b in combinations(pos, 2): # collect letter values ... lv = [[c[x] for c in cands] for x in (a, b)] # ... that occur more than once lv = [{x for i, x in enumerate(v) if x in v[i + 1:]} for v in lv] # choose for each letter a value for p1, p2 in product(*lv): # corresponding candidates if len(cs := [c for c in cands if c[a] == p1 and c[b] == p2]) != 1: continue # select PRAYER print("answer:", ''.join(str(cs[0][i]) for i in [2, 3, 0, 6, 1, 3]))LikeLike