Two MIT math grads bump into each other while shopping at Fry’s. They haven’t seen each other in over 20 years.
First grad to the second: “How have you been?”
Second: “Great! I got married and I have three daughters now.”
First: “Really? How old are they?”
Second: “Well, the product of their ages is 72, and the sum of their ages is the same as the number on that building over there…”
First: “Right, ok… Oh wait… Hmm, I still don’t know.”
Second: “Oh sorry, the oldest one just started to play the piano.”
First: “Wonderful! My oldest is the same age!”
How old was the first grad’s daughter?
Solution
The possible ages ( factors of 72 ) and their sums are shown below:
Ages: Sum of ages:
1 1 72 74
1 2 36 39
1 3 24 28
1 4 18 23
1 6 12 19
1 8 9 18
2 2 18 22
2 3 12 17
2 4 9 15
2 6 6 14
3 3 8 14
3 4 6 13
We can deduce from the man’s confusion over the building number that this wasn’t enough information to solve the problem. The chart shows the sum 14 twice for two different age possibilities, which would explain how knowing the building number alone would not have given him the answer. The clue that the “oldest one” started to play the piano rules out “2 6 6” as an answer, because there is no “oldest”. Since the
first grad was certain with the piano clue, the first grad’s oldest daughter is 8. I’ll leave it up to the reader to figure out why this doesn’t necessarily mean the second grad’s oldest daughter was also 8.
2026 Update: Logic Deduction Puzzles — Constraint Propagation
Deduction puzzles (“the oldest plays piano, the youngest has blue eyes…”) are essentially constraint satisfaction problems (CSPs). Learning to model them systematically leads to elegant solutions for complex variants.
from itertools import permutations
def solve_csp(variables, domains, constraints):
"""
Generic backtracking CSP solver.
variables: list of variable names
domains: dict mapping variable → list of possible values
constraints: list of functions (assignment → bool) that must hold
"""
assignment = {}
def is_consistent(var, value):
test = {**assignment, var: value}
return all(c(test) for c in constraints if all(v in test for v in c.__code__.co_varnames[:c.__code__.co_argcount]))
def backtrack():
if len(assignment) == len(variables):
return assignment.copy()
var = next(v for v in variables if v not in assignment)
for value in domains[var]:
if is_consistent(var, value):
assignment[var] = value
result = backtrack()
if result:
return result
del assignment[var]
return None
return backtrack()
# Example: Classic "who owns the fish" (Einstein's riddle) setup
# Simplified version with 3 siblings and 3 attributes
def oldest_piano_puzzle():
"""
Three siblings: Alice, Bob, Carol (oldest to youngest).
Instruments: piano, violin, guitar.
Clues: oldest plays piano, youngest doesn't play violin.
Find: who plays what?
"""
siblings = ['Alice', 'Bob', 'Carol'] # Alice=oldest, Carol=youngest
instruments = ['piano', 'violin', 'guitar']
for perm in permutations(instruments):
assignment = dict(zip(siblings, perm))
# Clue 1: Oldest (Alice) plays piano
if assignment['Alice'] != 'piano':
continue
# Clue 2: Youngest (Carol) doesn't play violin
if assignment['Carol'] == 'violin':
continue
print(f"Solution: {assignment}")
oldest_piano_puzzle()
# Alice: piano, Bob: violin, Carol: guitar
# Alice: piano, Bob: guitar, Carol: violin ← fails clue 2
# → Unique solution: Alice=piano, Bob=violin, Carol=guitar
# Systematic approach using arc consistency (AC-3)
def ac3_simplify(domains, constraints_by_pair):
"""
AC-3 algorithm: enforce arc consistency before backtracking.
Reduces domain sizes, often solves CSPs without backtracking.
"""
from collections import deque
queue = deque(constraints_by_pair.keys())
while queue:
(xi, xj) = queue.popleft()
if revise(domains, xi, xj, constraints_by_pair[(xi, xj)]):
if not domains[xi]:
return False # No solution
for xk in [v for v in domains if v != xj]:
queue.append((xk, xi))
return True
def revise(domains, xi, xj, constraint):
revised = False
for val_i in domains[xi][:]:
if not any(constraint(val_i, val_j) for val_j in domains[xj]):
domains[xi].remove(val_i)
revised = True
return revised
Real-world CSP applications:
- Scheduling: Assign time slots to meetings with conflict constraints (Google Calendar optimization)
- Register allocation: Assign CPU registers to variables in compilers (graph coloring = CSP)
- Sudoku: Each cell is a variable; rows, columns, and boxes are all-different constraints
- N-Queens: No two queens attack each other = CSP with arc consistency reducing search space