# Enigmatic Code

Programming Enigma Puzzles

## Enigma 277: Professor Calendar’s sons

From New Scientist #1424, 4th October 1984 [link]

Professor Calendar (1930-82) was a noted mathematician. The almanacs published by him were well known. A census enumerator once asked him about the ages of his sons. The professor said: “All my three sons have the same date of birth and were born on the same day of the week”. He continued, “there are no twins and the date of today is the sum of their ages”. The enumerator, an able mathematician himself, replied: “Sir, I could not get their ages. Is one of them twice as old as his brother?”. The professor replied in the affirmative.

What was the age of the eldest son?

[enigma277]

### 8 responses to “Enigma 277: Professor Calendar’s sons”

1. Jim Randell 1 May 2015 at 8:14 am

This Python program considers all possible dates between 1930 and 1982, and groups them by (day of the week, day of the month, month of the year). Then for each group it considers three of the dates, works out possible ages for those dates such that the sum of the ages is between 1 and 31, and groups the ages by their sum.

The sets of ages are then examined to find a sum that corresponds to more than one group, and exactly one of the groups contains an age that is twice that of another age in the group.

It runs in 306ms.

```from datetime import date
from collections import defaultdict
from itertools import combinations
from enigma import irange, printf

# record dates by (day of week, day, month)
r = defaultdict(list)

# consider dates in the professors lifetime
d = date(1930, 1, 1)
n = d.toordinal()
while d.year < 1983:
r[(d.weekday(), d.day, d.month)].append(d)
n += 1
d = date.fromordinal(n)

# find possible ages, recorded by sum (which is < 32)
ages = defaultdict(set)
for (k, ds) in r.items():
# choose three of the dates for the birthdays
for (A, B, C) in combinations(ds, 3):
# calculate the age differences (from C)
(dA, dB) = (C.year - A.year, C.year - B.year)
# now consider possible ages for the youngest
for n in irange(0, (31 - dA - dB) // 3):
(a, b, c) = (n + dA, n + dB, n)
ages[a + b + c].add((a, b, c))

# now consider possible age sums, there must be multiple possibilities
for (k, vs) in ages.items():
if len(vs) < 2: continue
# find ages where one brother is twice as old as a sibling
s = list(v for v in vs if any(x == 2 * y for (x, y) in combinations(v, 2)))
# and this must uniquely identify the actual solution
if len(s) != 1: continue

printf("ages={s} [sum={k}]")
```

Solution: The eldest son is 16 years old.

The middle son is 10, and the youngest son is 5 (exactly half the age of the middle son), so we know that the Census Enumerator visited on the 31st of the month.

There are five possible age sums that correspond to multiple ages – 23, 26, 28, 29, 31. But only when the sum is 31 is there a group where one sibling is twice the age of another.

The only other option for a visit on the 31st of the month is that the ages of the sons are (18, 12, 1), but in this scenario none of the siblings is twice the age of one of the other siblings. So the Census Enumerator is able to use his question to distinguish the two cases.

There are many possible dates that correspond to ages (16, 10, 5). For example, the birthdays could be on Sunday 24th May in 1953, 1959 and 1964, and the Census Enumerator visited on Halloween, Friday 31st October 1969.

2. Hugh Casement 1 May 2015 at 1:33 pm

In the UK, a census is taken in years that end in 1, generally at the end of March or beginning of April.
I think we can assume that the professor must have been at least 21 to have had three sons, none of them twins!  But are the dates 1930-1982 significant?

• Hugh Casement 2 May 2015 at 11:20 am

Between 1901 and 2099 a given date recurs on the same day of the week after successively 5, 6, 11, 6 years: a cycle of 28 = 4×7 years.
Possible ages are 0 + 5 + 11 = 16, 0 + 6 + 11 = 17, 1 + 6 + 12 = 19, 1 + 7 + 12 = 20, 2 + 7 + 13 = 22, 1 + 6 + 17 = 24, 3 + 8 + 14 = 25, 3 + 9 + 14 = 26, 3 + 8 + 19 = 30 (no confusion); or 0 + 6 + 17 = 23, 2 + 8 + 13 = 23; 0 + 5 + 22 = 27, 2 + 7 + 18 = 27; 0 + 11 + 17 = 28, 4 + 9 + 15 = 28; 0 + 6 + 23 = 29, 4 + 10 + 15 = 29; 1 + 12 + 18 = 31, 5 + 10 + 16 = 31.  Have I forgotten any?
If the enumerator had called on the 23rd or 27th or 28th or 29th he would have asked a different question, such as “is your youngest son less than a year old?”
It must have been 31 March 1971 or 1981.

I have to say that “the date of today” is strange wording.

• Jim Randell 3 May 2015 at 1:07 pm

I don’t think 1971 works, so if we’re limiting possible dates to those close to the official dates of the UK Census, that leaves Tuesday 31st March 1981 as the most likely date for a visit. The children would have been born on the same date in March 1965, 1971 and 1976. The Professor would be around 34, 40 and 45 at the time of these births.

The children being born on the same date in March 1945, 1951 and 1956 works for Census Enumerator visiting on Friday 31st March 1961. But the Professor would have to have been around 14 at the time of the birth of the first child, so let’s discard that solution.

The Wikipedia page on Census in the UK [ http://en.wikipedia.org/wiki/Census_in_the_United_Kingdom ] mentions that there was a trial census in 1966, so that makes a visit date of Thursday 31st March 1966 a possibility. The children could be born on the same date in January or February of 1950, 1956 and 1961, or April to December 1949, 1955, 1960. The Professor would have been around 19 when the first child was born, so this would work too.

3. Brian Gladman 2 May 2015 at 1:20 pm

This is basically the same as Jim’s solution.

```from itertools import combinations, product
from datetime import datetime
from collections import defaultdict

# consider dates between 1930 and 1982 inclusive
# and collect dates within years that are on the
# same day of the week
bdays = defaultdict(list)
for y, m, d in product(range(1930, 1983), range(1, 13), range(1, 32)):
try:
dt = datetime(y, m, d)
bdays[(d, m, dt.weekday())] += [dt]
except ValueError:
pass

# now look for three birth dates that can give ages
# that add to the day of the month
ages  = defaultdict(set)
for k in bdays:
for a, b, c in combinations(bdays[k], 3):
# c is the youngest - find the age differences
# for the other two relative to c
ac, bc = (c.year - a.year), (c.year - b.year)
# the sum of the three ages (3 * c_age + ac + bc)
# is equal to the day in the month so ...
c_age, r = divmod(k - ac - bc, 3)
if c_age > 0 and not r:
# store the three ages indexed on their sum
t = c_age, c_age + bc, c_age + ac
ages[sum(t)].update([t])

for sum_ages, list_ages in ages.items():
# we cannot determine the three ages at this point
if len(list_ages) > 1:
# look for one son being twice as old as another
for abc in list_ages:
if any(2 * x in abc for x in abc):
print('The eldest was {0:} from {0:}.'.format(abc))
```
4. Julian Gray 3 May 2015 at 9:59 am

Is there another solution? The 1961 census date was 23 April. The three boys could have been born in March or April in 1949 (age 12), 1955 (age 6) and 1960 (age 1), total ages 19, and the enumerator could have made his enquiries on 19 March or on 19 April. The prof would have been 19 or so when his firstborn appeared.

• Jim Randell 3 May 2015 at 11:53 am

Ages of (12, 6, 1) can’t be a solution to the problem because it is the only possibility for an age sum of 19, so the enumerator would be able to work out the ages without further questioning.

• Julian Gray 3 May 2015 at 12:19 pm

Thank you Jim, for your patience too.

This site uses Akismet to reduce spam. Learn how your comment data is processed.