Teaser 2460: [Birthdays]
From The Sunday Times, 15th November 2009 [link]
Seven friends whose first names were Ben, Cecil, Dion, Echo, Flor, Gobbins and Hugh had surnames Jack, Kain, Lack, Mines, Neal, O’Hara and Platts in some order.
They were all born on different days of the week. For each person, if you look at any two of their first name, their surname and the day of their birth, then there is just one letter of the alphabet that occurs in both.
Who was born on Monday (first name and surname)?
This puzzle was originally published with no title.
[teaser2460]
Jim Randell 10:45 am on 19 September 2025 Permalink |
This is the earliest of Graham Smithers “grouping” style puzzles that I have found.
Some time ago, I added the [[
grouping]] functionality in enigma.py specifically to solve this kind of puzzle (see: Teaser 2816), and it can be used to solve this puzzle.This Python program runs in 65ms. (Internal runtime is 828µs).
Solution: Flor Lack was born on Monday.
The full list is:
The full list of “grouping” style puzzles I’ve found is (2009 – 2019):
LikeLike
GeoffR 3:52 pm on 27 September 2025 Permalink |
An interesting experience using AI for this teaser.
ChatGPT Plus produced an initial solution for this teaser. Then I asked it to speed up the original solution, and it produced all the code in this posting to give a run-time of less than 1 millisecond! – apart from the Timer code from the enigma.py library. I the quizzed AI why this backtracking solution was so fast and about other solution techniques. ChatGPT then reverted to the basic AI model.
AI response below:
—————–
Summary for This Teaser
———————–
Problem type: Matching people (forename + surname) to days with constraints.
Applicable techniques:
———————
Backtracking (fastest)
Forward checking / constraint propagation
Heuristic ordering (most constrained first)
Permutation filtering / brute force
CSP solvers
Manual logic deduction
Why backtracking is fastest:
—————————
Incremental assignments with immediate constraint checks → prunes invalid branches early.
Recursive structure matches problem naturally.
Can combine heuristics to further reduce search space.
Other viable methods: Brute force, CSP solvers, logical deduction — but backtracking is the simplest and fastest in Python for this teaser.
# ST2460 ver 2 - ChatGPT Plus from enigma import Timer timer = Timer('ST2460', timer='E') timer.start() import itertools first_names = ["Ben", "Cecil", "Dion", "Echo", "Flor", "Gobbins", "Hugh"] surnames = ["Jack", "Kain", "Lack", "Mines", "Neal", "O'Hara", "Platts"] days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] # Condition: For each person, any two of (first, surname, day) # must share exactly one common letter. def valid_person(first, surname, day): f, s, d = set(first.lower()), set(surname.lower()), set(day.lower()) def one_common(a, b): return len(a & b) == 1 return one_common(f, s) and one_common(f, d) and one_common(s, d) # Precompute valid surname - day pairs for each first name day_options = {} for f in first_names: options = [] for s in surnames: for d in days: if valid_person(f, s, d): options.append((s, d)) day_options[f] = options # Backtracking search def backtrack(idx, used_surnames, used_days, assignment): if idx == len(first_names): print("Solution found:") for f in first_names: s, d = assignment[f] print(f" {f} {s} — {d}") return True f = first_names[idx] for s, d in day_options[f]: if s not in used_surnames and d not in used_days: assignment[f] = (s, d) if backtrack(idx + 1, used_surnames | {s}, \ used_days | {d}, assignment): return True del assignment[f] return False backtrack(0, set(), set(), {}) timer.stop() timer.report() ''' Solution found: Ben Kain — Tuesday Cecil Jack — Wednesday Dion Neal — Saturday Echo Mines — Thursday Flor Lack — Monday Gobbins Platts — Friday Hugh O'Hara — Sunday [ST2460] total time: 0.0004500s (450.00us) '''LikeLike
Jim Randell 10:46 pm on 27 September 2025 Permalink |
@Geoff: The posted code doesn’t work for me (no output). The
valid_person()andone_common()functions don’t look right.LikeLike
GeoffR 10:59 pm on 27 September 2025 Permalink |
@ Jim: It worked OK on my laptop, but not when I tested it on my desktop.
In view of your reservations, I think it best to remove my posting for now.
Will have another look at the AI code.
LikeLike
GeoffR 10:03 am on 28 September 2025 Permalink |
Maybe it was my error copying, reformatting, or posting the original code.
Errors were found today by ChatGPT on re- submitting the code to it with a request to find any errors. Here is AI’s reply and the updated code:
Mistakes:
1) valid_person defines f, s, d but doesn’t actually return a result.
2) The return inside one_common after the first return is unreachable.
3)The logic for checking conditions belongs inside valid_person, not one_common.
# ST 2460 - ChatGPT_update 28-9-25 from enigma import Timer timer = Timer('ST2460', timer='E') timer.start() import itertools first_names = ["Ben", "Cecil", "Dion", "Echo", "Flor", "Gobbins", "Hugh"] surnames = ["Jack", "Kain", "Lack", "Mines", "Neal", "O'Hara", "Platts"] days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] # Condition: For each person, any two of (first, surname, day) # must share exactly one common letter. def one_common(a, b): return len(a & b) == 1 def valid_person(first, surname, day): f, s, d = set(first.lower()), set(surname.lower()), set(day.lower()) return one_common(f, s) and one_common(f, d) and one_common(s, d) # Precompute valid surname - day pairs for each first name day_options = {} for f in first_names: options = [] for s in surnames: for d in days: if valid_person(f, s, d): options.append((s, d)) day_options[f] = options # Backtracking search def backtrack(idx, used_surnames, used_days, assignment): if idx == len(first_names): print("Solution found:") for f in first_names: s, d = assignment[f] print(f" {f} {s} — {d}") return True f = first_names[idx] for s, d in day_options[f]: if s not in used_surnames and d not in used_days: assignment[f] = (s, d) if backtrack(idx + 1, used_surnames | {s}, \ used_days | {d}, assignment): return True del assignment[f] return False backtrack(0, set(), set(), {}) timer.stop() timer.report() ''' Solution found: Ben Kain — Tuesday Cecil Jack — Wednesday Dion Neal — Saturday Echo Mines — Thursday Flor Lack — Monday Gobbins Platts — Friday Hugh O'Hara — Sunday [ST2460] total time: 0.1103383s (110.34ms) '''LikeLike