Teaser 2832: A New Year reminiscence
From The Sunday Times, 1st January 2017 [link] [link]
Whilst filing away last year’s diary this morning I came across an old diary from my teenage years. In it I can see that in one particular month I went to four parties, three of them being on Saturdays and the other on a Sunday. I wrote down the four dates of the parties in words (in the format “January first” etc.) and found that each of the dates used a different prime number of letters.
What were the four dates that I wrote down?
[teaser2832]






Jim Randell 11:16 am on 4 November 2021 Permalink |
This Python program finds the first (most recent) year and month satisfying the conditions.
It runs in 55ms.
Run: [ @replit ]
from datetime import date from enigma import (irange, int2words, catch, is_prime, subsets, join, sprintf as f, cached, printf) # map month numbers to English names months = dict(enumerate([ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ], start=1) ) # ordinals that aren't cardinal + "th", or cardinal - "y" + "ieth" _ordinal = { 1: 'first', 2: 'second', 3: 'third', 5: 'fifth', 8: 'eighth', 9: 'ninth', 12: 'twelfth', } # return the ordinal of a number (0 < n < 100) @cached def ordinal(n): if n in _ordinal: return _ordinal[n] if n < 20: return int2words(n) + 'th' (t, r) = divmod(n, 10) if r == 0: return int2words(n)[:-1] + 'ieth' else: return int2words(n - r) + ' ' + ordinal(r) # format dates fmt = lambda x: join((f('"{d}" {n}') for (d, n) in x), sep=", ", enc="[]") # find solutions (going back in time) def solve(month=12, year=2016): while year > 0: # find saturdays and sundays in the specified month sats = list() suns = list() for day in irange(1, 31): d = catch(date, year, month, day) if d is None: break wd = d.weekday() if not (wd == 5 or wd == 6): continue s = months[month] + " " + ordinal(day) n = sum(1 for x in s if x.isalpha()) if is_prime(n): (sats if wd == 5 else suns).append((s, n)) # choose three saturdays for sat in subsets(sats, size=3): ps = set(p for (s, p) in sat) if len(ps) != 3: continue sun = list((s, p) for (s, p) in suns if p not in ps) if sun: yield (month, year, sat, sun) # move back a month month -= 1 if month == 0: (month, year) = (12, year - 1) # find the first solution for (month, year, sat, sun) in solve(): printf("month={month}, year={year}, sat={sat}, sun={sun}", sat=fmt(sat), sun=fmt(sun)) breakSolution: The four dates are: August fifth, August twelfth, August twenty sixth, August twenty seventh.
With 11, 13, 17, and 19 letters respectively.
The first viable year (counting back from 2016) is 2006.
If we suppose the setter was 16 when attending the parties that gives an age in 2016 of 26.
But there are further solutions going back in time (but they all give the same answer to the puzzle):
I won’t speculate on the probable age of the setter.
LikeLike
Frits 2:31 pm on 4 November 2021 Permalink |
Some constants have been taken from Brian Gladman’s site.
I didn’t bother to put in code for leap years (28/29 in February, the month always has to be August anyway).
import datetime MONTHS = ( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ) DAYS = ( "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth", "fouteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth","nineteenth", "twentieth", "twentyfirst", "twentysecond", "twentythird","twentyfourth", "twentyfifth", "twentysixth", "twentyseventh", "twentyeighth", "twentyninth", "thirtieth", "thirtyfirst" ) WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] P = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29} # days in month (29 for February) DIM = ( 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ) minm = min(len(m) for m in MONTHS) maxm = max(len(m) for m in MONTHS) mind = min(len(d) for d in DAYS) maxd = max(len(d) for d in DAYS) primes = [x for x in range(minm + mind, maxm + maxd + 1) if x in P] if len(primes) < 4: print("no solution") exit() # skip months that are too short to make the 4th prime ms = [m for m in MONTHS if len(m) + maxd >= primes[3]] # skip months that are too long to make the 1st prime ms = [m for m in ms if len(m) + mind <= primes[-4]] # pick one value from each entry of a <k>-DIMensional list <ns> def pickOneFromEach(k, ns, s=[]): if k == 0: s = sorted(s) # first entry must be either a day later than the rest or same week day if tuple(sorted((s[0] - x) % 7 for x in s[1:])) in \ {(1, 1, 1), (0, 0, 6)}: yield s else: for n in ns[k-1]: # day numbers must be equal or one apart if not s or all((n - x) % 7 in {0, 1, 6} for x in s): yield from pickOneFromEach(k - 1, ns, s + [n]) # process all viable months for m in ms: lenm = len(m) mno = MONTHS.index(m) + 1 # make list of different month+day lengths lst = [[] for _ in range(4)] for i, d in enumerate(DAYS[:DIM[mno] - 1], start=1): totlen = lenm + len(d) if totlen not in primes: continue lst[primes.index(totlen)].append(i) # check whether we can find a combination of <lst> entries with # three same week days and one on the following day cands = list(pickOneFromEach(4, lst)) for c in cands: for y in range(2016, 1900, -1): wdays = [WEEKDAYS[datetime.date(year=y, month=mno, day=d).weekday()] for d in c] if sorted(wdays) == ['Saturday', 'Saturday', 'Saturday', 'Sunday']: print(f"{y}, {m} {c}") break # stop at first solutionLikeLike