Design a site like this with WordPress.com

## Teaser 2750: Granny’s birthdays

At Granny’s birthday this year she was telling us some surprising things about some past birthdays. She told us that each year she writes down the date of her birthday (in the eight-digit form dd/mm/yyyy) and her age in years.

On two occasions in her lifetime it has turned out that this has involved writing each of the digits 0 to 9 exactly once. The first of these occasions was in 1974.

What is Granny’s date of birth (in the eight-digit form)?

Note that in order to solve this puzzle it is important to be aware of the date it was originally set.

All 200 puzzles included in the book The Sunday Times Brain Teasers Book 1 (2019) are now available on S2T2.

[teaser2750]

• #### Jim Randell 8:00 am on 6 October 2022 Permalink | Reply

The puzzle was originally set on 7th June 2015, and mentions Granny’s birthday “this year” as having already happened. So her birthday must be earlier in the year than 7th June.

This Python program considers dates in 1974 up to 6th June, if the date includes 8 different digits, then the remaining 2 digits indicate Granny’s age (in some order), and we can then look for other years.

The program runs in 59ms. (Internal runtime is 4.3ms)

Run: [ @replit ]

```from datetime import (date, timedelta)
from enigma import (irange, repeat, nsplit, union, subsets, nconcat, printf)

digits = set(irange(0, 9))

# consider dates earlier than this
end = date(1974, 6, 7)

# consider dates in 1974
inc = lambda x, i=timedelta(days=1): x + i
for d in repeat(inc, date(1974, 1, 1)):
if not (d < end): break
# remaining digits
ds4 = union([nsplit(d.month, 2), nsplit(d.day, 2)])
ds = digits.difference(nsplit(d.year, 4), ds4)
if len(ds) != 2: continue
# construct possible ages
for ss in subsets(ds, size=2, select='P'):
age = nconcat(ss)
year = 1974 - age
# collect "special" years
years = list()
for bd in irange(10, 99):
y = year + bd
if y > 2015: break
if not digits.difference(nsplit(y, 4), ds4, nsplit(bd, 2)):
years.append(y)
if len(years) == 2 and years[0] == 1974:
# output solution
printf("{d} -> {years}", d=date(year, d.month, d.day).strftime("%d/%m/%Y"))
```

Solution: Granny’s date of birth is: 26/05/1936.

So Granny is 38 in 1974 → (26/05/1974, 38).

And she is 47 in 1983 → (26/05/1983, 47).

In 2015 (when the puzzle was set) she was 79.

If we consider all dates in 1974 then there is a further solution of: 25/06/1936.

So, the puzzle only has a unique solution if posed between 27th May and 24th June. A fact that was not mentioned when the puzzle was included in the book The Sunday Times Brain Teasers 1 (2019).

Like

## Teaser 2751: Red-handed

I removed an even number of red cards from a standard pack and I then divided the remaining cards into two piles. I drew a card at random from the first pile and it was black (there was a whole-numbered percentage chance of this happening). I then placed that black card in the second pile, shuffled them, and chose a card at random from that pile. It was red (and the percentage chance of this happening was exactly the same as that earlier percentage).

How many red cards had I removed from the pack?

[teaser2751]

• #### Jim Randell 8:11 am on 4 October 2022 Permalink | Reply

This Python program runs in 56ms. (Internal runtime is 1.7ms).

Run: [ @replit ]

```from enigma import (irange, cproduct, div, printf)

R = B = 26

# remove an even (non-zero) number of red cards from the pack
for k in irange(2, R, step=2):

# pile 1 has r1 reds and b1 blacks (at least 1 black)
for (r1, b1) in cproduct([irange(0, R - k), irange(1, B)]):

# chance of drawing a black is a whole number percentage
pb = div(100 * b1, r1 + b1)
if pb is None: continue

# a black card is moved to pile 2 which now has r2 reds and b2 blacks
(r2, b2) = (R - k - r1, B - b1 + 1)
# chance of a red card is also pb percent
pr = div(100 * r2, r2 + b2)
if pr is None or pr != pb: continue

# output solution
printf("k={k}: p1={r1}r + {b1}b -> pb={pb}%; p2={r2}r + {b2}b -> pr={pr}%")
```

Solution: 8 red cards were removed from the pack.

From the 18 red and 26 black cards the two piles can be made in two ways:

pile 1: 6 red + 24 black; P(black) = 24/30 = 80%
(1 black card is moved to pile 2)
pile 2: 12 red + 3 black; P(red) = 12/15 = 80%

pile 1: 12 red + 3 black; P(black) = 3/15 = 20%
(1 black card is moved to pile 2)
pile 2: 6 red + 24 black; P(red) = 6/30 = 20%

Like

## Teaser 2747: Marble jar

At our local fete one of the games consisted of guessing the number of marbles in a jar: some of the marbles were red and the rest were blue. People had to guess how many there were of each colour.

The organiser gave me a couple of clues. Firstly, he told me that there were nearly four hundred marbles altogether. Secondly, he told me that if, when blindfolded, I removed four marbles from the jar, then the chance that they would all be red was exactly one in a four-figure number.

How many red marbles were there, and how many blue?

[teaser2747]

• #### Jim Randell 8:49 am on 29 September 2022 Permalink | Reply

If there are T marbles in total (T = “nearly 400”) and R of them are red, then the probability of removing 4 red marbles is:

P = (R / T) × ((R − 1) / (T − 1)) × ((R − 2) / (T − 2)) × ((R − 3) / (T − 3))
P = (R × (R − 1) × (R − 2) × (R − 3)) / (T × (T − 1) × (T − 2) × (T − 3))

And this probability is “one in a four-figure number”, so the largest it can be is 1/1000 and the smallest is 1/9999.

The following Python program considers total numbers of marbles from 399 down to 350, and then looks for numbers of red marbles that give an appropriate value for P.

It runs in 57ms. (Internal run time is 1.2ms).

Run: [ @replit ]

```from enigma import (irange, printf)

# consider total number of marbles (nearly 400)
for T in irange(399, 350, step=-1):
# denominator of P
d = T * (T - 1) * (T - 2) * (T - 3)
# R = number of red marbles
for R in irange(4, T):
# numerator of P
n = R * (R - 1) * (R - 2) * (R - 3)
# calculate p = 1/P
(p, x) = divmod(d, n)
if p > 9999: continue
if p < 1000: break
if x == 0:
# output solution
printf("red = {R}, blue = {B}; total = {T} -> P = 1/{p}",B=T - R)
```

Solution: There were 45 red marbles and 342 blue marbles.

So, 387 marbles in total. And the probability of choosing 4 red is 1/6176.

Like

## Teaser 2749: Round the river

My school holds “Round the river” runs — a whole number of metres to a bridge on the river and then the same number of metres back. Some years ago I took part with my friends Roy, Al, David and Cy. We each did the outward half at our own steady speeds (each being a whole number of centimetres per minute). For the return half I continued at my steady speed, Roy increased his speed by 10%, Al increased his speed by 20%, David increased his by 30%, and Cy increased his by 40%. We all finished together in a whole number of minutes, a little less than half an hour.

What (in metres) is the total length of the race?

[teaser2749]

• #### Jim Randell 8:46 am on 27 September 2022 Permalink | Reply

The speeds are whole numbers of centimetres per minute, so we will work in units of centimetres and minutes.

If the distance to bridge is x centimetres, then x must be divisible by 100.

And the time is t minutes (less than 30).

If the 5 speeds are: a, b, c, d, e, then we have:

t = x/a + x/a = 2x / a
t = x/b + x/1.1b = 21x / 11b
t = x/c + x/1.2c = 11x / 6c
t = x/d + x/1.3d = 23x / 13d
t = x/e + x/1.4e = 12x / 7e

From which we see that x must also be divisible by 11, 6, 13, 7.

Placing some sensible limits on distance and speeds, we can suppose x is less than 10km (= 10,000m = 1,000,000cm), and that speeds are less than 15mph (≈ 40,000cm/min).

This Python program runs in 61ms. (Internal runtime is 129µs).

Run: [ @replit ]

```from enigma import (irange, mlcm, div, printf)

# x must be a multiple of m
m = mlcm(100, 11, 6, 13, 7)

# consider possible total times: "a little less than half an hour"
for t in irange(29, 21, step=-1):

# consider possible values of x (up to 10km)
for x in irange(m, 1000000, step=m):

# calculate the speeds
vs = [
div(20 * x, 10 * t),
div(21 * x, 11 * t),
div(22 * x, 12 * t),
div(23 * x, 13 * t),
div(24 * x, 14 * t),
]
# check speeds
if None in vs or vs[-1] * 1.4 > 40000: continue

# output solution
printf("d={d} metres [t={t} x={x} {vs}]", d=div(2 * x, 100))
```

Solution: The total length of the race is 6006m.

And the race took exactly 25 minutes.

The outward speeds are:

1: 24024 cm/min (≈ 8.96 mph); 12.5 min out + 12.5 min back (@ 8.96 mph)
2: 22932 cm/min (≈ 8.55 mph); 13.1 min out + 11.9 min back (@ 9.40 mph)
3: 22022 cm/min (≈ 8.21 mph); 13.6 min out + 11.4 min back (@ 9.85 mph)
4: 21252 cm/min (≈ 7.92 mph); 14.1 min out + 10.9 min back (@ 10.30 mph)
5: 20592 cm/min (≈ 7.68 mph); 14.6 min out + 10.4 min back (@ 10.75 mph)

Note that the speeds on the return leg are not all whole numbers of cm/min.

Like

## Teaser 2742: Hymns bored

Peter became bored during the Sunday service, so his mind turned to the three three-figure hymn numbers displayed on the board, all chosen from the five hundred hymns in the hymnal. He noticed that the sum of the digits for each hymn was the same, that one hymn number was the average of the other two, and that no digit appeared more than once on the board.

What (in increasing order) were the three hymn numbers?

[teaser2742]

• #### Jim Randell 9:39 am on 22 September 2022 Permalink | Reply

We can treat this as an alphametic puzzle, and use the [[ `SubstitutedExpression` ]] solver from the enigma.py library to solve the puzzle.

The following run file executes in 79ms. (Internal runtime of the generated program is 10.2ms).

Run: [ @replit ]

```#! python3 -m enigma -r

SubstitutedExpression

# if the hymn numbers are: ABC, DEF, GHI

# the hymns are in order
"A < D < G"

# hymns are numbered from 1 to 500
"G < 5"

# each hymn has the same digit sum
"all_same(A + B + C, D + E + F, G + H + I)"

# one hymn number is the average of the other two: DEF = (ABC + GHI) / 2
"div(ABC + GHI, 2) = DEF"

--invalid=""
```

Solution: The hymn numbers are: 157, 283, 409.

Like

• #### GeoffR 12:00 pm on 22 September 2022 Permalink | Reply

```
from itertools import permutations

for p1 in permutations('1234567890', 9):
A, B, C, D, E, F, G, H, I = p1
if '0' in (A, D, G):continue

# check digit sums are same for three numbers
total = int(A) + int(B) + int(C)
if int(D) + int(E) + int(F) != total:continue
if int(G) + int(H) + int(I) != total:continue

# find three hymn numbers in increasing order
ABC, DEF = int(A + B + C), int(D + E + F)
GHI = int(G + H + I)
if not ABC < DEF < GHI:continue
# check all hymn numbers are less than 500
if not all( x < 500 for x in (ABC, DEF, GHI)):continue

# One hymn number was the average of the other two
if 2 * DEF == ABC + GHI:
print(f"Three hymn numbers are {ABC}, {DEF} and {GHI}.")

# Three hymn numbers are 157, 283 and 409.

```

Like

• #### GeoffR 1:23 pm on 22 September 2022 Permalink | Reply

```% A Solution in MiniZinc
include "globals.mzn";

var 1..9:A; var 0..9:B; var 0..9:C;
var 1..9:D; var 0..9:E; var 0..9:F;
var 1..9:G; var 0..9:H; var 0..9:I;

constraint all_different([A,B,C,D,E,F,G,H,I]);

var 100..500:ABC = 100*A + 10*B + C;
var 100..500:DEF = 100*D + 10*E + F;
var 100..500:GHI = 100*G + 10*H + I;

constraint A + B + C == D + E + F /\ A + B + C == G + H + I;
constraint ABC < DEF /\ DEF < GHI;
constraint 2 * DEF == ABC + GHI;

solve satisfy;
output ["Three hymn numbers were " ++ show(ABC) ++ ", " ++ show(DEF)
++ " and " ++ show(GHI) ];

% Three hymn numbers were 157, 283 and 409
% ----------
% ==========

```

Like

## Teaser 2744: The school run

Each of the three houses of Merryhouse School entered four students in the cross-country race. Points were awarded with 12 for the winner, 11 for second, and so on down to 1 for the tail-ender (from Berry House). When the points were added up, all houses had equal points. Three of the runners from Cherry House were in consecutive positions, as were just the two middle-performers from Derry House.

Which house did the winner come from, and what were the individual scores of its runners?

[teaser2744]

• #### Jim Randell 7:38 am on 20 September 2022 Permalink | Reply

There are tri(12) = 78 points awarded, so each of the three houses gets 26 points.

We can treat this puzzle as an alphametic problem in base 13, distributing values 1-12 to each of the runners, and use the [[ `SubstitutedExpression` ]] solver from the enigma.py library to solve it.

When the puzzle says that 3 from team C and 2 from team D are consecutive, I assumed that this implies that exactly the specified number are consecutive for those teams. (For a looser interpretation we can change the `!=` at line 31 to an `or`).

The following run file executes in 67ms. (The internal run time of the generated program is 265µs).

Run: [ @replit ]

```#! python3 -m enigma -r

# suppose points are:
#
#       1  2  3  4
#  B =  E  F  G  H
#  C =  I  J  K  L
#  D =  M  N  P  Q

SubstitutedExpression

# allocate points 1-12
--base=13
--digits=1-12

# teams are in order
"E > F > G > H"  # team B
"I > J > K > L"  # team C
"M > N > P > Q"  # team D

# team B has the tail-ender
"H = 1"

# each team had the same number of points (= 26)
"E + F + G + H == 26"
"I + J + K + L == 26"
"M + N + P + Q == 26"

# team C has 3 consecutive points
"J == K + 1"
"(I == J + 1) != (K == L + 1)"

# team D has middle 2 consecutive points
"N == P + 1"
"M > N + 1"
"P > Q + 1"

--template=""
```

Solution: The winner was from Derry House, which was awarded 12 + 6 + 5 + 3 points.

The points awarded are:

B: 11 + 10 + 4 + 1 = 26
C: 9 + 8 + 7 + 2 = 26
D: 12 + 6 + 5 + 3 = 26

Like

## Teaser 2735: Two to choose

I told Sue a two-figure number and I told Terry another two-figure number, one of which was a multiple of the other. I explained this to them but knew that neither of them would be able to work out the other number. (In fact, if they had to guess the other number Sue had three times as many choices as Terry — but I did not tell them that).

When Sue confirmed that it was impossible for her to work out Terry’s number, he was then able to work out her number.

What were their numbers?

[teaser2735]

• #### Jim Randell 8:47 am on 15 September 2022 Permalink | Reply

In the following Python program I construct a map from 2-digit numbers to possible “other” numbers (other 2-digit numbers that are proper multiples or divisors of the first number).

We know (but S and T do not) that neither of them can work out the others number, so we can remove those numbers that only have a single associated number from consideration.

When S reveals she cannot work out T’s number, T knows that S’s number must be one of these candidates, so the associated numbers for T’s number must include exactly one of the candidate numbers.

The program runs in 57ms. (Internal run time is 204µs).

Run: [ @replit ]

```from enigma import (defaultdict, irange, singleton, intersect, printf)

# collect 2-digit numbers that are proper multiples of other 2-digit numbers
d = defaultdict(list)
for a in irange(10, 49):
for b in irange(2 * a, 99, step=a):
d[a].append(b)
d[b].append(a)

# candidates that have multiple other numbers
ks = set(k for (k, vs) in d.items() if len(vs) > 1)

# T can work out S's number (knowing S cannot work out T's)
for T in ks:
ss = d[T]
S = singleton(intersect([ks, ss]))
if S is not None:
ts = d[S]
# in the solution we are looking for S has 3x as many initial choices as T
if len(ts) == 3 * len(ss):
printf("S={S} [-> {ts}; T={T} [-> {ss}]")
```

Solution: Sue’s number was 14. Terry’s number was 98.

We have: 98 = 7 × 14.

S has 14, so she knows that T has one of {28, 42, 56, 70, 84, 98}.

And T has 98, so initially he knows that S has one of {14, 49}. So S has three times as many options for T as T has for S.

But if S had 49 there are no 2-digit divisors and the only 2-digit multiple is 98, so S would be able to work out T’s value. And as S cannot work out T’s value she cannot have 49, so T can deduce that she must have 14.

Like

• #### Frits 11:18 am on 15 September 2022 Permalink | Reply

```
# candidates that have multiple other numbers
cands = {x: lst for x in range(10, 100)
if len(lst := [y for y in range(10, 100)
if x != y and not(y % x and x % y)]) > 1}

# Terry must have 2 candidates and Sue 6 for the "three times" requirement
for terry, sues in cands.items():
if len(sues) != 2: continue
# only one of Terry's numbers for Sue must be ambiguous
if len([sue := s for s in sues if s in cands]) != 1 or \
len(cands[sue]) != 6: continue
print("Terry =", terry, "Sue =", sue)
```

or

```
# Terry must have 2 candidates and Sue 6 for the "three times" requirement

# candidates that have two or six other numbers
cands = {x: lst for x in range(10, 100)
if len(lst := [y for y in range(10, 100)
if x != y and not(y % x and x % y)]) in {2, 6}}

for sue, terries in cands.items():
if len(terries) != 6: continue
# find Terry's where <sue> is one of the two candidates
for terry, sues in cands.items():
if len(sues) != 2 or sue not in sues: continue
# determine other candidate (not <sue>)
other = [s for s in sues if s != sue][0]
# <other> may not have multiple other numbers
if len([x for x in range(10, 100) if x != other and
not(other % x and x % other)]) > 1: continue
print("Terry =", terry, "Sue =", sue)
```

Like

## Teaser 2728: Garden design

I have a square garden with sides a whole number of metres in length. It is surrounded by a fence with posts at the corners and then at one metre intervals. I wish to make the garden into four triangular beds surrounding a lawn that has four sides of different lengths. To mark out the lawn I choose one post on each of the sides of the garden and I stretch a length of string around those four posts. I can create my lawn in various ways but the length of string needed is always one of two possible values. I have chosen one arrangement using the smaller of the two lengths.

What is the area of my lawn?

[teaser2728]

• #### Jim Randell 10:08 am on 13 September 2022 Permalink | Reply

In this program I round results (in metres) to 4 decimal places, to get measurements to with sub-millimetre accuracy, but we don’t have to worry about floating point inaccuracies when we compare measurements.

This Python program runs in 53ms. (Internal run time is 1.0ms).

Run: [ @replit ]

```from enigma import (defaultdict, subsets, irange, hypot, seq_all_different, tuples, printf)

# measure float quantities to 4dp
measure = lambda x: round(x, 4)

# look for solutions with an <n> by <n> square
def solve(n, k=None):
# choose the positions of the posts
d = defaultdict(set)
for ps in subsets(irange(1, n - 1), size=4, select="M"):
# perpendicular dimensions of corner triangles
ts = list((x, n - y) for (x, y) in tuples(ps, 2, circular=1))
# calculate the sides of the central quadrilateral
ds = list(hypot(x, y) for (x, y) in ts)
# they should all be different lengths
if not seq_all_different(ds, fn=measure): continue
# calculate the total length of string required
s = measure(sum(ds))
# caclulate area
a = measure(n * n - 0.5 * sum(x * y for (x, y) in ts))
# early termination?
if k and len(d.keys()) > k: return
if k and len(d.keys()) != k: return
return d

# consider an n x n garden
for n in irange(3, 100):
d = solve(n, 2)
if d is None: continue
# output solution (= shortest length string)
s = min(d.keys())
for a in sorted(d[s]):
printf("n={n}: lawn area = {a:.3f} m^2 [string length = {s:.3f} m]")
break  # we only need the first garden
```

Solution: The area of the lawn is: 7 square metres.

Considering gardens where the central lawn area has sides of 4 different lengths.

For a garden less than 4m × 4m this is not possible.

For a 4m × 4m garden there are essentially 2 different layouts (where the central area has sides of different length):

The lengths of string, and area of lawn are:

A: string = √5 + √10 + √13 + √ 8 ≈ 11.832 m; lawn = 8.5 m²
B: string = √2 + √13 + √5 + √18 ≈ 11.498 m; lawn = 7.0 m²

So layout B uses the least amount of string, and so gives the answer to the puzzle.

For a garden greater than 4m × 4m there are more than 2 different layouts, which give more than 2 different lengths of string.

Like

## Teaser 2740: X times

In this long multiplication sum, I am multiplying a three-figure number by itself. Throughout the workings one particular digit has been replaced by X wherever it occurs: all other digits have been replaced by a question mark.

What is the three-figure number being squared?

[teaser2740]

• #### Jim Randell 9:21 am on 8 September 2022 Permalink | Reply

The intermediate multiplications are presented in the opposite order to how I was taught long multiplication. But from the layout of the columns the intent is obvious.

We can use the [[ `SubstitutedExpression` ]] solver from the enigma.py library to the solve this puzzle.

The following run file executes in 73ms. (Internal runtime of the generated program is 277µs).

Run: [ @replit ]

```#! python3 -m enigma -r

#       a X b
#  *    a X b
#   ---------
#   c d e      = aXb * a
#   f g X X    = aXb * X
#     h i j k  = aXb * b
#   ---------
#   X m n p q  = aXb * aXb

SubstitutedExpression

# added symbols are different from X
--distinct="aX,bX,cX,dX,eX,fX,gX,hX,iX,jX,kX,mX,nX,pX,qX"

# the multiplication sum
"{aXb} * {aXb} = {Xmnpq}"
"{aXb} * {a} = {cde}"
"{aXb} * {X} = {fgXX}"
"{aXb} * {b} = {hijk}"

```

Solution: The number being squared is: 286.

Note that you will find the answer even if you don’t check that the question marks are all different from X (we can use [[ `--distinct=""` ]] to show this), but without this check the solution is not complete.

Like

• #### GeoffR 1:33 pm on 8 September 2022 Permalink | Reply

```
from itertools import permutations

for p1 in permutations('1234567890', 3):
a, X, b = p1
aXb = int(a + X + b)
# check leading digit of multiplication result
res = aXb * aXb
if res //10000 != int(X):continue

# 1st line of multiplication
line1 = int(a) * aXb * 100
if X in set(str(line1)):continue

# 2nd line of multiplication
line2 = int(X) * aXb * 10
if line2 //100 % 10 != int(X):continue
if line2//10 % 10 != int(X):continue

# 3rd line of multiplication
line3 = int(b) * aXb
if X in set(str(line3)):continue
print(f"The three-figure number being squared is {aXb}.")

# The three-figure number being squared is 286.

```

Like

## Teaser 2752: Best before

Peter likes to note “pandigital” times, such as 15.46, 29/03/78, that use all ten digits. Here the five individual numbers (15, 46, 29, 3 and 78) have a product that is divisible by the perfect square 36, and they also have a sum that is two more than a perfect square.

He has been watching for pandigital times ever since and remembers one where the product of the five numbers was not divisible by any perfect square (apart from 1): this has never happened since! He is also looking out for a pandigital time where the sum of the five numbers is a perfect square:

(a) When was that last “non-square” pandigital time?
(b) When will that first “square-sum” pandigital time be?

[teaser2752]

• #### Jim Randell 8:51 am on 6 September 2022 Permalink | Reply

We generate possible pandigital times, and then check to find the last (before the puzzle was set) with a square-free product, and the first (after the puzzle was set) with a perfect square sum.

This Python program runs in 70ms. (Internal runtime is 14.4ms).

Run: [ @replit ]

```from datetime import datetime
from enigma import (
irange, choose, implies, nconcat, catch,
multiply, is_square_free, is_square, printf
)

# find pandigital (y, m, d, H, M) values
def generate():
# possible digits
digits = set(irange(0, 9))

# selection functions
fns = [
# month first digit; is 0-1
lambda m1: m1 < 2,
# hour first digit; is 0-2
lambda m1, H1: H1 < 3,
# day first digit; is 0-3
lambda m1, H1, d1: d1 < 4,
# minute first digit; is 0-5
lambda m1, H1, d1, M1: M1 < 6,
# month second digit; m2 = 1 -> 0, 2
lambda m1, H1, d1, M1, m2: implies(m1 == 1, m2 < 3),
# hour second digit; H1 = 2 -> 0, 1, 2
lambda m1, H1, d1, M1, m2, H2: implies(H1 == 2, H2 < 3),
# day second digit; d1 = 3 -> 0, 1
lambda m1, H1, d1, M1, m2, H2, d2: implies(d1 == 3, d2 < 2),
# remaining digits (M2, y1, y2) = any
None, None, None
]

# assign digits
for (m1, H1, d1, M1, m2, H2, d2, M2, y1, y2) in choose(digits, fns, distinct=1):
(y, m, d, H, M) = (nconcat(*x) for x in [(y1, y2), (m1, m2), (d1, d2), (H1, H2), (M1, M2)])
y += (2000 if y < 78 else 1900)
t = catch(datetime, y, m, d, H, M)
if t is not None:
yield (t, (H, M, d, m, y % 100))

# date of the puzzle
now = datetime(2015, 6, 21)

a = b = None

# look for pandigital times
for (t, ns) in generate():
# (a) latest time before now with a square-free product
if t < now and (a is None or t > a) and is_square_free(multiply(ns)):
a = t
# (b) earliest time after now with a perfect square sum
if t > now and (b is None or t < b) and is_square(sum(ns)):
b = t

# output solution
fmt = lambda t: t.strftime("%H:%M, %d/%m/%y")
printf("(a) {a}", a=fmt(a))
printf("(b) {b}", b=fmt(b))
```

Solution: (a) 15:43, 26/07/89; (b) 18:59, 27/06/34.

Like

• #### Frits 6:06 pm on 6 September 2022 Permalink | Reply

Two programs:

```
from itertools import permutations

# small numbers which are square free
nums = [n for n in range(1, 32) if n % 11 and all(n % y for y in [4, 9, 25])]
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
mx_dt = 0

# check day/month/hour permutations
for d, m, h in permutations(nums, 3):
if m > 12 or h > 24: continue

s = "".join(str(x).zfill(2) for x in (d, m, h))
if len(set(s)) != 6: continue          # different digits

p1 = d * m * h                         # product
# not divisible by any perfect square
if not all(p1 % (n * n)
for n in [2] + list(range(3, int(p1**.5) + 1, 2))): continue

# check day number
if d > 28 and m == 2:
if year % 4 == 0 and (year % 100 or year % 400 == 0):
days[1] = 29
else:
days[1] = 28
if d > days[m - 1]:  continue

rest = [int(x) for x in range(9, -1, -1) if str(x) not in s]
mdh = str(m).zfill(2) + str(d).zfill(2) + str(h).zfill(2)

# check year/minutes permutations
for p in permutations(rest):
if p[2] > 5: continue                # minutes < 60

year = 10 * p[0] + p[1]
if 15 < year < 78: continue          # year between 1978 and 2015
mins = 10 * p[2] + p[3]

# built date string
dt = int(("19" if year > 77 else "20") + str(year) +  mdh + str(mins))

if dt < mx_dt: continue              # skip if earlier year than maximum

# not divisible by any perfect square for new numbers ...
p2 = mins * year
if not all(p2 % (n * n)
for n in [2] + list(range(3, int(p2**.5) + 1, 2))): continue
# ... and for all five 2-digit numbers
p2 *= p1
if not all(p2 % (n * n)
for n in [2] + list(range(3, int(p2**.5) + 1, 2))): continue

mx_dt = dt                           # new maximum
break                                # as p is decreasing

s = str(mx_dt)
print(f"last 'non-square' pandigital time: "
f"{s[6:8]}/{s[4:6]}/{s[:4]} {s[8:10]}:{s[10:]}")
```

and

```
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

# pick one value from each entry of a <k>-dimensional list <ns>
# so that all digits in the <k> values are different
def pickOneFromEach(k, ns, s=[], dgts=set()):
if k == 0:
yield s
else:
for n in ns[k - 1]:
sn = str(n).zfill(2)
if all(x not in dgts for x in sn):
yield from pickOneFromEach(k - 1, ns, s + [sn], dgts | set(sn))

# months, days and hours have to use the digits 0, 1 and 2
# months 10 and 12 are invalid as they use two of the digits 0, 1 and 2
# and leave no room for day and hour so month < 10
ys = list(n for n in range(34, 100)
if n % 11 and all(y not in str(n) for y in "012"))
ms = list(range(1, 10))
ds = list(n for n in range(13, 32) if n % 11 and n % 10)
hs = list(n for n in range(24) if n % 11 and n % 10)
mis = list(n for n in range(34, 60)
if n % 11 and all(y not in str(n) for y in "012"))

rev_lst = [mis, hs, ds, ms, ys]

for p in pickOneFromEach(5, rev_lst):
s = "".join(p)
# sum has to be a perfect square
if sum([int(x) for x in p]) not in {144, 169, 196}: continue

# check day number
m, d = (int(p[1]), int(p[2]))
if d > 28 and m == 2:
if year % 4 == 0 and (year % 100 or year % 400 == 0):
days[1] = 29
else:
days[1] = 28
if d > days[m - 1]:  continue

print(f" first 'square-sum' pandigital time: "
f"{p[2]}/{p[1]}/{p[0]} {p[3]}:{p[4]}")
break       # we have found the first date
```

Like

## Teaser 2732: Prime meat

Mark has recently converted from vegetarianism. John sent him a coded message consisting of a list of prime numbers. Mark found that by systematically replacing each digit by a letter the list became the message:

EAT BEEF AT TIMES
IT IS A PRIME MEAT

What number became PRIME?

[teaser2732]

• #### Jim Randell 9:29 am on 1 September 2022 Permalink | Reply

We can use the [[ `SubstitutedExpression()` ]] solver from the enigma.py library to solve this puzzle.

The following Python program executes in 58ms. (Internal runtime of the generated program is 1.8ms).

Run: [ @replit ]

```from enigma import (SubstitutedExpression, sprintf)

words = "EAT BEEF AT TIMES IT IS A PRIME MEAT"

SubstitutedExpression(
list(sprintf("is_prime({w})") for w in words.split()),
template=words,
).run()
```

Solution: PRIME = 80429.

Like

• #### GeoffR 11:53 am on 1 September 2022 Permalink | Reply

This solution gets the answer, but is a bit slow – approx 1.5 sec.

```% A Solution in MiniZinc
include "globals.mzn";

predicate is_prime(var int: x) =
x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x)))))
((i < x) -> (x mod i > 0));

var 0..9:E; var 0..9:A; var 0..9:T; var 0..9:B;
var 0..9:F; var 0..9:I; var 0..9:M; var 0..9:S;
var 0..9:P; var 0..9:R;

constraint E > 0 /\ B > 0 /\I > 0 /\ P > 0
/\ T > 0 /\ A > 0 /\ M > 0;

constraint all_different ([E, A, T, B, F, I, M, S, P, R]);

var 100..999:EAT = 100*E + 10*A + T;
var 1000..9999:BEEF = 1000*B + 110*E + F;
var 10..99:AT = 10*A + T;
var 10..99:IS = 10*I + S;
var 10..99:IT = 10*I + T;
var 10000..99999:TIMES = 10000*T + 1000*I + 100*M + 10*E + S;
var 10000..99999:PRIME = 10000*P + 1000*R + 100*I + 10*M + E;
var 1000..9999:MEAT = 1000*M + 100*E + 10*A + T;

constraint sum([is_prime(A), is_prime(EAT), is_prime(BEEF),
is_prime(AT), is_prime(TIMES), is_prime(IT), is_prime(IS),
is_prime(PRIME), is_prime(MEAT)]) == 9;

solve satisfy;
output ["PRIME = " ++ show(PRIME) ++
"\n" ++ "[E, A, T, B, F, I, M, S, P, R] = "
++ show( [E, A, T, B, F, I, M, S, P, R] ) ];

% PRIME = 80429
% ----------
%  E, A, T, B, F, I, M, S, P, R] =
% [9, 5, 3, 6, 1, 4, 2, 7, 8, 0]
% ==========
```

Like

• #### GeoffR 1:56 pm on 1 September 2022 Permalink | Reply

```from itertools import permutations
from enigma import is_prime

for p1 in permutations('1234567890', 5):
A, E, I, T, S = p1
if '0' in (A, E, I, T):continue
if not(is_prime(int(A))):continue
AT = int(A + T)
if not (is_prime(AT)):continue
IS = int(I + S)
if not (is_prime(IS)):continue
IT = int(I + T)
if not (is_prime(IT)):continue
EAT = int(E + A + T)
if not (is_prime(EAT)):continue
q = set('1234567890').difference([A, E, I, T, S])
for p2 in permutations(q):
B, F, M, P, R = p2
if '0' in (B, M, P):continue
BEEF = int(B + E + E + F)
if not (is_prime(BEEF)):continue
TIMES = int(T + I + M + E + S)
if not (is_prime(TIMES)):continue
MEAT = int(M + E + A + T)
if not (is_prime(MEAT)):continue
PRIME = int( P + R + I + M + E)
if is_prime(PRIME):
print(f"PRIME = {PRIME}")

# PRIME = 80429

```

Like

## Teaser 2743: Line-up

I have arranged the numbers from 1 to 27 in a 3-by-3-by-3 cubical array. I have noticed that the nine numbers making up one of the faces of the cube are all primes.

Also, I have searched through the array and written down the sum of any three numbers that are in a straight line. I have then calculated the grand total of all those line-sums. It turns out that the grand total is itself a perfect cube!

What is that grand total?

[teaser2743]

• #### Jim Randell 9:32 am on 30 August 2022 Permalink | Reply

Considering a standard 3×3×3 Rubik’s cube, we can think of is as consisting of 27 cubelets:

8 corner pieces (with 3 colours)
12 edge pieces (with 2 colours)
6 middle pieces (with 1 colour)
1 core piece (with 0 colours)

And if we consider the number of lines each type of cubelet appears in we have:

corner = 7 lines
edge = 4 lines
middle = 5 lines
core = 13 lines

The grand total then consists of:

7× (8 corner pieces)
4× (12 edge pieces)
5× (6 centre pieces )
13× (1 core piece)

And as long as we distribute the numbers amongst the same type of piece any arrangement will have the same grand total. (Although to confirm with the layout specified in the puzzle you have to keep the primes altogether on one face).

There are only nine primes between 1 and 27 (= 2, 3, 5, 7, 11, 13, 17, 19, 23) and these are arranged on one of the faces, so we have 4 corners, 4 edges, and 1 centre that prime.

So we just need to split the primes into sets of size (4, 4, 1) with sums (p1, p2, p3), and the remaining 18 non-primes into sets of size (4, 8, 5, 1) with sums (n1, n2, n3, n4), such that:

T = 7(p1 + n1) + 4(p2 + n2) + 5(p3 + n3) + 13 n4 [= a perfect cube]

This can be written as:

T = 4(p1 + p2 + p3 + n1 + n2 + n3 + n4) + 3(p1 + n1) + (p3 + n3) + 9 n4

and we know that: (p1 + p2 + p3 + n1 + n2 + n3 + n4) = tri(27), so:

T = 4 tri(27) + 3(p1 + n1) + (p3 + n3) + 9 n4

So the maximum possible value for T is 2363, giving a maximum possible cube of 13³ = 2197.

And the minimum possible value for T is 1731, giving a minimum possible cube of 13³ = 2197.

So the only possibility for a grand total that is a cube is 13³ = 2197.

Solution: The grand total is 2197.

But now we need to show there is at least one solution.

If we do find a solution we can permute the numbers amongst their own groups without changing the grant total to give a class with (4! × 4! × 1!) × (4! × 8! × 5! × 1!) = 66886041600 solutions. (Which we can divide by 8 to account for reflections/rotations).

This Python program chooses a non-prime core value, and partitions the primes into sets of size (4, 1) and the remaining non-primes into sets of size (4, 5), and in each case the contribution to the grand total is recorded. We then look for pairs of contributions that can make a grand total that is a perfect cube. (There are many of these, so we stop when we have found a single example for a particular cube).

It runs in 13s and finds there are many possible core values that lead to viable solutions (and each of these has many partitions sums that lead to many solutions).

Run: [ @replit ]

```from enigma import (irange, primes, subsets, intc, intf, cbrt, cb, printf)

# partition set <s> into subsets with sizes in <ns>
def partition(s, ns, ss=[]):
if not ns:
yield ss
elif len(ns) == 1:
for x in subsets(s, size=ns[0]):
yield ss + [x]
else:
for x in subsets(s, size=ns[0]):
yield from partition(s.difference(x), ns[1:], ss + [x])

# primes
ps = set(primes.between(1, 27))

# non-primes
nps = set(irange(1, 27)).difference(ps)

# total of primes and non-primes (= tri(27))
t = sum(ps) + sum(nps)

# record grand totals
Ts = set()

# split the primes into sets of size (4, 1) and record the contribution to the total
pss = set(3 * sum(p1) + sum(p3) for (p1, p3) in partition(ps, (4, 1)))
printf("[{n} prime partitions]", n=len(pss))

# choose the core value (non-prime)
for v in nps:
printf("[core={v}]")

# split the remaining non-primes into sets of size (4, 5) and record the contribution to the total
nss = set(3 * sum(n1) + sum(n3) for (n1, n3) in partition(nps.difference({v}), (4, 5)))
printf("[{n} non-prime partitions]", n=len(nss))

# find possible cubes
t0 = 4 * t + 9 * v
cubes = set(cb(x) for x in irange(intc(cbrt(t0 + min(pss) + min(nss))), intf(cbrt(t0 + max(pss) + max(nss)))))
printf("[possible cubes = {cubes}]", cubes=sorted(cubes))

# find pairs from pss and nss that give a cube grand total
for T in cubes:
for p in pss:
n = T - t0 - p
if n > 0 and n in nss:
printf("[p={p} n={n} -> T={T}]")
break  # we only need one example for this T

# output solution
printf("grand total = {Ts}")
```

There are many, many ways to place the numbers on the cube.

Here is one (this has 8 as the core value – the smallest possible, but it can be any non-prime value between 8 and 27):

In this solution the different types of cubelet are (prime + non-prime values):

8 corners = (7, 17, 19, 23) + (24, 25, 26, 27); sum = 168
12 edges = (2, 3, 5, 11) + (1, 4, 6, 9, 10, 12, 14, 16); sum = 93
6 middles = (13) + (15, 18, 20, 21, 22); sum = 109
1 core = () + (8); sum = 8

And so the grand total is:

7×168 + 4×93 + 5×109 + 13×8 = 2197 = 13³

Like

## Teaser 2745: Square cut

Jorkens, the wily old cricketer, is faced with a new type of square cut. His house has three square bedrooms, all of different sizes. He has just bought a new carpet for the largest bedroom and has cut up its old carpet into four rectangular pieces, the smallest of which has an area of four square metres. He is able to use the four pieces to carpet the other two bedrooms exactly.

What is the area of the largest bedroom?

[teaser2745]

• #### Jim Randell 10:46 am on 25 August 2022 Permalink | Reply

Suppose the floors of the rooms have sides a, b, c (smallest to largest).

Then, if the carpet from the largest bedroom can be used to exactly carpet the other 2 bedrooms we have:

c² = a² + b²

So, (a, b, c) form a right-angled triangle (but this is not necessarily a Pythagorean triple, as we are not told that the sides of the rooms take on integer values).

Or:

b² = c² − a²
⇒ b² = (c − a)(c + a)

Each side of the larger square must be reduced (otherwise we have a rectangle with side c that won’t fit in the smaller bedrooms).

Assuming the carpet is cut into exactly 4 rectangular regions (which may be square), we must have cuts like this:

(i.e. one cut must go across the entire length of the square, and the rectangles produced from this cut must both have cuts perpendicular to this).

I supposed we cut off an a × a square for the small room, which leaves three rectangles remaining to assemble into a square for the middle room.

This gives us 3 remaining pieces of size (for some value d):

a × (c − a)
(c − a) × d
(c − a) × (c − d)

And in order to be assembled into a square of side b one of the pieces must have a dimension of b.

So either: b = (c − a) or b = d.

If b = (c − a) we infer that b = c, which is not possible, so the 3 remaining pieces are:

a × (c − a)
(c − a) × b
(c − a) × (c − b)

Which fit together like this:

From which we see (vertical edges):

b = a + (c − b)
⇒ 2b = a + c
⇒ b = (a + c) / 2

i.e. b is the mean of a and c.

So we write:

b = a + x
c = a + 2x

To give the following diagram:

From which we see (horizontal edges):

a + x = 4x
⇒ a = 3x

So:

a = 3x
b = 4x
c = 5x

(So (a, b, c) is a Pythagorean triple, if we measure it in units of x).

And the pieces and their areas are:

a × a = 9x²
a × (c − a) = 6x²
b × (c − a) = 8x²
(c − b) × (c − a) = 2x²

And the smallest of these (2x²) has an area of 4 square metres hence x² = 2.

And we want the area of the largest bedroom (c²).

c² = (5x)²
⇒ c² = 25x²
⇒ c² = 50 square metres

Solution: The floor area of the largest bedroom is 50 square metres.

And we can calculate the floor areas of the other rooms as well:

small = 18 square metres (a 3√2 metre square)
middle = 32 square metres (a 4√2 metre square)
large = 50 square metres (a 5√2 metre square)

And 18 + 32 = 50.

The 50 square metre carpet from the largest room is cut into the following rectangles:

√2 × 2√2 (≈ 1.41 × 2.83) = 4 square metres (b.1)
2√2 × 3√2 (≈ 2.83 × 4.24) = 12 square metres (b.2)
2√2 × 4√2 (≈ 2.83 × 5.66) = 16 square metres (b.3)
3√2 × 3√2 (≈ 4.24 × 4.24) = 18 square metres (a)

Or with each square having an area of 2 square metres (i.e. of side √2 metres):

Like

## Teaser 2738: Prime day for the Irish

St Patrick’s Day is March 17 and it is a prime day in many ways:

What number month? 3;
What number day? 17;
How many letters in “March”? 5;
How many days in March? 31.

I asked Pat the same questions about his birthday this year — but I simply asked whether the four answers were prime or not. When he had told me he said: “Now, if I told you its day of the week this year, you should be able to work out my birthday”.

Then, without me actually being told the day, I was indeed able to work out his birthday.

What is his birthday?

[teaser2738]

• #### Jim Randell 10:42 am on 23 August 2022 Permalink | Reply

This Python program runs in 57ms. (Internal run time is 1.7ms).

Run: [ @replit ]

```from datetime import (date, timedelta)
from enigma import (repeat, is_prime, filter_unique, unpack, printf)

# number of letters in each month (English names)
months = (
"january", "february", "march", "april", "may", "june", "july",
"august", "september", "october", "november", "december",
)
mlen = dict((k, len(x)) for (k, x) in enumerate(months, start=1))

# number of days in each month
mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

# generate dates in 2015, return (<date>, <answers-to-questions>)
def generate():
inc = lambda x, i=timedelta(days=1): x + i
# consider consecutive days in 2015
for d in repeat(inc, date(2015, 1, 1)):
if d.year > 2015: break
# are the following prime?
qs = tuple(is_prime(x) for x in (
# 1. the month number
d.month,
# 2. the day number
d.day,
# 3. the number of letters in the month
mlen[d.month],
# 4. the number of days in the month
mdays[d.month],
))
yield (d, qs)

# candidate dates
rs = generate()
# selection functions
fn_d = unpack(lambda d, qs: d)
fn_qs = unpack(lambda d, qs: qs)
fn_qs_dow = unpack(lambda d, qs: (qs, d.isoweekday()))

# if we knew the answers to the questions _and_ the day of the week
# then we could work out the date
rs = filter_unique(rs, fn_qs_dow, fn_d).unique

# now we can work out the date only knowing the answers to the questions
rs = filter_unique(rs, fn_qs, fn_d).unique

# output solutions
for (d, qs) in rs:
printf("date = {d} [qs={qs}]", d=d.strftime('%A, %d %B %Y'))
```

Solution: Pat’s birthday is on 29th November.

There are 9 dates that can be identified as unique knowing the answers to the questions and the day of the week (in 2015).

If the answers to the questions are: (not prime, prime, prime, not prime) we have:

Mon ⇒ 13th April 2015
Tue ⇒ 7th April 2015
Wed ⇒ 29th April 2015
Sat ⇒ 11th April 2015

If the answers to the questions are: (prime, prime, not prime, prime) we have:

Mon ⇒ 13th July 2015
Tue ⇒ 7th July 2015
Wed ⇒ 29th July 2015
Sat ⇒ 11th July 2015

And if the answers to the questions are: (prime, prime, not prime, not prime) we have:

Sun ⇒ 29th November 2015

As the setter does not need to be told the day of the week to find the birthday from these 9 he must have been told (prime, prime, not prime, not prime) so the candidate dates are narrowed down to a single possibility.

Like

• #### Frits 2:48 pm on 23 August 2022 Permalink | Reply

partition_unique has recently been updated.

```
from calendar import month_name, monthrange, day_name, weekday

# https://brg.me.uk/?page_id=4800
from partition_unique import partition_unique

# primes up to 365
P = {3, 5, 7, 11, 13, 17, 19}
P |= {2} | {x for x in range(23, 366, 2) if all(x % p for p in P)}

year = 2015
sols = []
# generate dates in 2015
# store (<answers-to-questions>, <day name>, <(month, day)>)
for month in range(1, 13):
month_length = monthrange(year, month)[1]
len_month_name = len(month_name[month])
for d in range(1, month_length + 1):
# find the day of the week
d_name = day_name[weekday(year, month, d)]

sols.append(((# 1. the month number
month in P,
# 2. the day number
d in P,
# 3. the number of letters in the month
len_month_name in P,
# 4. the number of days in the month
month_length in P),
d_name, (month, d)
))

f_answs = lambda answs, nm, md: answs
f_answs_nm = lambda answs, nm, md: (answs, nm)
f_md = lambda answs, nm, md: md

# find those solutions for which the answers plus day of the week
sols = partition_unique(sols, f_answs_nm, f_md)[0]

# find those solutions for which the answers lead to the date
sols = partition_unique(sols, f_answs, f_md)[0]

for _, _, md in sols:
```

Like

• #### NigelR 5:19 pm on 25 August 2022 Permalink | Reply

Not, I suspect, very ‘pythonic’ but seems to run ok. enigma timer gives 3.2ms:

```prim=[2,3,5,7,11,13,17,19,23,29,31]
mths=["january", "february", "march", "april", "may", "june", "july",
"august", "september", "october", "november", "december"]
days=[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
dow=['We','Th','Fr','Sa','Su','Mo','Tu']  # 1 Jan 2015 fell on a Thursday
#i = month (0-11),j = day of month.  Returns string of day of week (Mo-Su)
day = lambda i,j: dow[(j+sum([days[k] for k in range(i)]))%7]
#generate answers to questions + day of week for each day in format 'n/p n/p n/p n/p dd'
ans=[[''.join(['p' if x+1 in prim else 'n','p' if y in prim else 'n', 'p' if len(mths[x]) in prim else 'n',
'p'if days[x] in prim else 'n',day(x,y)]),x,y] for x in range(12) for y in range (1,days[x]+1)]
#create dictionary with count of each occurrence of 'n/p n/p n/p n/p dd'
cand={x[0]:[y[0] for y in ans].count(x[0]) for x in ans }
#remove non-unique entries
lst=[x for x in cand.keys() if cand[x] == 1]
#identify whether there is a unique day of week entry:
result = [x for x in lst if [y[4:] for y in lst].count(x[4:])==1]
if len(result)>1:
print('No unique solution found')
exit()
print([['Birthday is' + ' '+str( x[2]) + ' '+ str(mths[x[1]+1].capitalize())] for x in ans if x[0]==result[0]][0][0])
```

Like

## Teaser 2729: Factorial fact

The “factorials” of numbers (denoted by !) are defined by:

1! = 1
2! = 2 × 1
3! = 3 × 2 × 1
4! = 4 × 3 × 2 × 1
etc.

It is possible to take eleven of the twelve factorials 1!, 2!, 3!, 4!, 5!, 6!, 7!, 8!, 9!, 10!, 11!, 12! and to split them into groups of three, four and four, so that in each group the product of the factorials in that group is a perfect square.

What are the factorials in the group whose product is the smallest?

[teaser2729]

• #### Jim Randell 9:07 am on 18 August 2022 Permalink | Reply

This Python program runs in 56ms. (Internal run time is 1.68ms).

Run: [ @replit ]

```from enigma import (
irange, subsets, factorial, multiply, is_square_p,
seq_all_different as all_different, printf
)

# measure function
fn = lambda ns: multiply(factorial(n) for n in ns)

# find groups of k-tuples with a measure that is a square
def generate(k):
for ns in subsets(irange(1, 12), size=k):
if is_square_p(fn(ns)):
yield ns

# collect 3-, 4-tuples
n3s = list(generate(3))
n4s = list(generate(4))

# choose two disjoint 4-tuples
for (g1, g2) in subsets(n4s, size=2):
if not all_different(g1 + g2): continue
# and find a disjoint 3-tuple
for g3 in n3s:
if not all_different(g1 + g2 + g3): continue
# find the group with the minimal measure
g = min(g1, g2, g3, key=fn)
printf("min = {g} [{g3} {g1} {g2}]")
```

Solution: The factorials in the group with the smallest product are: 1!, 8!, 9!.

There are 2 ways to form the groups:

(1, 8, 9) (2, 3, 11, 12) (4, 5, 7, 10)
(1, 8, 9) (2, 4, 11, 12) (3, 5, 7, 10)

Like

• #### GeoffR 2:38 pm on 18 August 2022 Permalink | Reply

A brute force solution produced products of 11, 14 and 18 digits long for groups of 3, 4 and 4 factorials. The solution ran in approximately 20 sec.

The smallest solution was repeated if the break statement is excluded.

```from itertools import permutations
from enigma import factorial as fact
from enigma import is_square as is_sq

# Precalculate the twelve factorials
F1,F2, F3 = fact(1), fact(2), fact(3)
F4, F5, F6 = fact(4), fact(5), fact(6)
F7, F8, F9 = fact(7), fact(8), fact(9)
F10, F11, F12 = fact(10), fact(11), fact(12)

# Factorial dictionary for output
ND = {F1: 'F1', F2:'F2', F3:'F3', F4:'F4', F5:'F5',F6:'F6',
F7:'F7', F8:'F8', F9:'F9', F10:'F10', F11:'F11', F12:'F12' }

for p1 in permutations((F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12),11):
a, b, c, d, e, f, g, h, i, j, k = p1
# split factorials into groups of three, four and four
if not (is_sq(a * b * c)): continue
if not (is_sq(d * e * f * g)): continue
if not( is_sq(h * i * j * k)):continue
# sort factorial products
T = sorted((a * b * c , d * e * f * g, h * i * j * k))
if a * b * c < d * e * f * g and a * b * c < h * i * j * k:
print(f"Factorials in smallest product are {ND[a]}, {ND[b]} and {ND[c]}.")
print(f"Smallest factorial product = {a * b * c}")
break

# Factorials in smallest product are F1, F8 and F9.
# Smallest factorial product = 14631321600

```

Like

• #### GeoffR 9:17 am on 19 August 2022 Permalink | Reply

A three stage permutation seemed more compatible with a (3,4,4) group of numbers. This solution was considerably faster than my previous posting, running in 4.64ms.

```from itertools import permutations
from enigma import factorial as fact, is_square as is_sq, timer

timer.start()
# Precalculate the twelve factorials
F1,F2, F3 = fact(1), fact(2), fact(3)
F4, F5, F6 = fact(4), fact(5), fact(6)
F7, F8, F9 = fact(7), fact(8), fact(9)
F10, F11, F12 = fact(10), fact(11), fact(12)

factorials = {F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12}

# Factorial dictionary for output
ND = {F1: 'F1', F2:'F2', F3:'F3', F4:'F4', F5:'F5',F6:'F6',
F7:'F7', F8:'F8', F9:'F9', F10:'F10', F11:'F11', F12:'F12' }

# first stage permutation
# split factorials into groups of three, four and four
for p1 in permutations(factorials, 3):
a, b, c = p1
if not (is_sq(a * b * c)):
continue

# second stage permutation
q1 = factorials.difference({a, b, c})
for p2 in permutations(q1, 4):
d, e, f, g = p2
if not (is_sq(d * e * f * g)):
continue

# third stage permutation
q2 = q1.difference({d, e, f, g})
for p3 in permutations(q2, 4):
h, i, j, k = p3
if not( is_sq(h * i * j * k)):
continue

# sort factorial products
T = sorted((a * b * c , d * e * f * g, h * i * j * k))
if (a * b * c) < (d * e * f * g) and (a * b * c) < (h * i * j * k):
print(f"Factorials in smallest product are {ND[a]}, {ND[b]} and {ND[c]}.")
print(f"Smallest factorial product = {a * b * c}")
timer.stop()
timer.report()
# only one solution needed
exit()

# Factorials in smallest product are F8, F1 and F9.
# Smallest factorial product = 14631321600
# [timing] total time: 0.0046427s (4.64ms)

```

Like

• #### NigelR 11:33 pm on 18 August 2022 Permalink | Reply

[timing] total time: 0.0030880s (3.09ms)

```from itertools import combinations as comb
from enigma import timer
timer.start()

def fac(num): #returns num!
return num*fac(num-1) if num > 1 else 1

def is_square(num): #returns True if num is square
return round(num ** 0.5) ** 2 == num

def lprod(lst) : #returns product of elements in list
res = 1
for x in lst:
res = res * x
return res

facs = {i:fac(i) for i in range(1,13)}
# identify group of i numbers between 1 and 12 where product of their factorial is a square
group = lambda i:[x for x in comb(facs.keys(),i) if is_square(lprod([facs[y] for y in x]))]
threes,fours = group(3),group(4)
#identify group of 3 and two groups of 4 where all 11 elements are different
valid = [[x,y[0],y[1]] for x in threes for y in comb(fours,2) if len(set(list(x)+[z for sub in y for z in sub]))==11]
valid = [y for x in valid for y in x] # flatten valid
prods =[lprod(list(x)) for x in valid] # generate products of factorials in valid group
res=valid[prods.index(min(prods))] #identify factorials with minimum product
print('Factorials in group with smallest product are:',*res)
timer.stop()
timer.report()
```

Factorials in group with smallest product are: 1 8 9

Like

## Teaser 2733: Letter-writing

Last year I went to calligraphy lessons. They were held weekly, on the same day each week, for nine consecutive months. Actually I only went to 15 of the lessons, and after the course was over I listed the dates of those lessons that I had attended. In order to practise my new skills I wrote the dates in words (in the format “First of January” etc.) and I found to my surprise that each date used a different number of letters.

What were the dates of the first and last lessons that I attended?

[teaser2733]

• #### Jim Randell 7:36 am on 9 August 2022 Permalink | Reply

The lessons were for “9 months”, so let’s suppose there were 39 weekly lessons, of which the setter attended 15.

This Python program runs in 68ms.

Run: [ @replit ]

```from datetime import (date, timedelta)
from enigma import (repeat, trim, int2words, group, subsets, cache, sprintf as f)

# map month numbers to English names
month = 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
@cache
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 trim(int2words(n), tail=1) + 'ieth'
return int2words(n - r) + ' ' + ordinal(r)

# return the length of the inscription for a date
@cache
def inscribe(d):
t = f("{d} of {m}", d=ordinal(d.day), m=month[d.month])
return sum(x.isalpha() for x in t)

# generate length <k> sequences of possible dates
def generate(k):
(i1, i7) = (timedelta(days=1), timedelta(days=7))
d = date(2014, 1, 1)
while True:
# generate 39 weeks of dates
ds = list(repeat((lambda d: d + i7), d, k - 1))
# are we done?
if ds[-1].year > 2014: break
yield ds
# increase start date
d += i1

# record possible date spans
rs = set()

# consider possible dates for 39 weekly lessons
for ds in generate(39):
# group dates by letter count
g = group(ds, by=(lambda d: inscribe(d)))
# choose 15 letter counts
for ks in subsets(g.keys(), size=15):
# find earliest and latest possible dates
dmin = min(min(g[k] for k in ks))
dmax = max(max(g[k] for k in ks))

# output solution(s)
for (dmin, dmax) in rs:
print(f("{dmin} -> {dmax}"))
```

Solution: The first lesson attended was on the 5th April. The final lesson attended was on 27th December.

In fact there is only one sequence of 39 weekly dates in 2014 that gives at least 15 different lengths:

10 letters: “Third of May”; “Tenth of May”
11 letters: “Fifth of July”
12 letters: “Fifth of April”
13 letters: “Seventh of June”; “Twelfth of July”; “Ninth of August”
14 letters: “Twelfth of April”; “Second of August”
15 letters: “Fourth of October”; “First of November”; “Sixth of December”
16 letters: “Seventeenth of May”; “Thirty first of May”; “Fourteenth of June”; “Nineteenth of July”; “Sixth of September”; “Eighth of November”
17 letters: “Nineteenth of April”; “Twenty fourth of May”; “Twenty first of June”; “Twenty sixth of July”; “Sixteenth of August”; “Thirtieth of August”; “Eleventh of October”
18 letters: “Twenty eighth of June”
19 letters: “Twenty third of August”; “Eighteenth of October”; “Fifteenth of November”; “Twentieth of December”
20 letters: “Twentieth of September”; “Twenty fifth of October”; “Thirteenth of December”
21 letters: “Thirteenth of September”; “Twenty ninth of November”
22 letters: “Twenty second of November”
23 letters: “Twenty seventh of December”
24 letters: “Twenty seventh of September”

And this gives exactly 15 different lengths of inscription. So, each length must be represented by a single date that the setter attended.

The period covered is 5th April – 27th December, and as 5th April is the only date with an inscription of 12 letters, and 27th December is the only date with an inscription of 23 letters, the setter must have attended the actual first and last lessons, and these give the answer to the puzzle.

Like

## Teaser 2725: Darts match

Andrew, Alexander, Austin, Anthony, Benjamin, Charles, Christopher, Elijah, Jacob, Jayden, Jackson, James, Jason, Mason, Michael, Nathan, Newman, Robert, Samuel and William entered a darts competition, arranged into five teams of four players. It turned out that, for any pair of members of any team, there were just two letters of the alphabet that occurred (once or more) in both their names. The names in each team were arranged alphabetically, the first name being the captain and the last name the reserve. Then the teams were numbered 1 to 5 in alphabetical order of the captains.

In the order 1 to 5, who were the reserves?

[teaser2725]

• #### Jim Randell 8:27 am on 4 August 2022 Permalink | Reply

This puzzle is slightly different from the usual kind of grouping puzzle, but we can still use some of the [[ `grouping` ]] functions from the enigma.py library.

This Python program runs in 57ms. (Internal runtime is 1.4ms).

Run: [ @replit ]

```from enigma import (grouping, join, printf)

# available players
names = [
'Andrew', 'Alexander', 'Austin', 'Anthony', 'Benjamin', 'Charles',
'Christopher', 'Elijah', 'Jacob', 'Jayden', 'Jackson', 'James', 'Jason',
'Mason', 'Michael', 'Nathan', 'Newman', 'Robert', 'Samuel', 'William',
]

# grouping function
fn = grouping.share_letters(2)

# form the names <ns> into teams of size <k>
def teams(ns, k, ts=[]):
# are we done?
if not ns:
yield ts
else:
# find the next captain
cap = min(ns)
# and 3 team members
for team in grouping.gang(k - 1, cap, ns.difference([cap]), fn):
team = [cap] + sorted(team)
# solve for the remaining names
yield from teams(ns.difference(team), k, ts + [team])

for ts in teams(set(names), 4):
for (i, team) in enumerate(ts, start=1):
printf("Team {i}: {team}", team=join(team, sep=", "))
printf()
```

Solution: The reserves are (Team 1-5): William, Samuel, Robert, Newman, Michael.

The full teams are:

Team 1: Alexander, Austin, James, William
Team 2: Andrew, Christopher, Jason, Samuel
Team 3: Anthony, Benjamin, Charles, Robert
Team 4: Elijah, Jackson, Nathan, Newman
Team 5: Jacob, Jayden, Mason, Michael

Like

• #### Frits 8:04 pm on 5 August 2022 Permalink | Reply

A lot more work but already solved before recursion (ie parameter teams will have 5 teams) .

```
from itertools import combinations

# check that each string shares exactly <k> letters
shr_lttrs = lambda s1, s2, k: len(set(s1.lower()) & set(s2.lower())) == k

# form the names <ns> into teams of size <k>
def form_teams(ns, k, ts=[]):
# are we done?
if not ns:
yield ts
else:
# find the name with the least matches
key = min({k: v for k, v in d.items() if k in ns}.items(),
key=lambda x: len(x[1]))
# and 3 team members
for rest in combinations([x for x in key[1] if x in ns], 3):
# these 3 names should have exactly 2 letters in common
if any(not y in d[x] for x, y in zip(rest, rest[1:])): continue

team = sorted((key[0], ) + rest)
# solve for the remaining names
yield from form_teams(ns.difference(team), k, ts + [team])

# available players
vs = [
'Andrew', 'Alexander', 'Austin', 'Anthony', 'Benjamin', 'Charles',
'Christopher', 'Elijah', 'Jacob', 'Jayden', 'Jackson', 'James', 'Jason',
'Mason', 'Michael', 'Nathan', 'Newman', 'Robert', 'Samuel', 'William',
]

vs = sorted(vs)

# create dictionary: name --> matches
d = {vs[i]: {x for j, x in enumerate(vs[:i] + vs[i + 1:])
if shr_lttrs(vs[i], x, 2)} for i in range(len(vs))}

teams = []
dsize = -1
# process until no more changes to dictionary
while dsize != sum(len(v) for v in d.values()):
dsize = sum(len(v) for v in d.values())

# if a match doesn't share exactly 2 letters with at least 2 other matches
# it can be removed from the dictionary as we we need 4 members in a team
d = {k: {x for x in v if sum(x != y and y in d[x] for y in v) > 1}
for k, v in d.items()}

# if only three matches left we have finalized a team
match3 = [x for k, v in list(d.items())
for x in [k] + list(v) if len(v) == 3]

# add these persons to teams (removing duplicates while preserving order)
teams += list(dict.fromkeys(match3))

# maintain dictionary for persons in teams
d = {k: [x for x in v if x not in teams]
for k, v in d.items() if k not in teams}

# reduce values for persons in teams
vs = [x for x in vs if x not in teams]

# solve for remaining people
for ts in form_teams(set(vs), 4):
i = 0
for (i, team) in enumerate(ts, start=1):
print(f"Team {i}: {', '.join(team)}")
for j in range(len(teams) // 4):
i += 1
print(f"Team {i}: {', '.join(sorted(teams[j * 4:j * 4 + 4]))}")
print()
```

Like

## Teaser 2726: Christmas star

I asked Peter to place the numbers 1 to 10 at the ten intersection points of the Christmas star so that in each of the five lines the four numbers added to the same total. He found that this was impossible so instead he did it with the numbers 1 to 9 together with just one of those digits repeated. In his answer there was just one line in which that digit did not appear.

[In ascending order] what were the four numbers in that line?

[teaser2726]

• #### Jim Randell 9:12 am on 28 July 2022 Permalink | Reply

I assume were are talking about a layout like this:

There are 5 lines, and each of the numbers appears on two of the lines.

If we add the lines we will be counting each number twice, and the total will be 5 times the common sum, T.

So, if X is the repeated digit we have:

2 × (1 + 2 + … + 9 + X) = 5 × T
T = 18 + (2/5)X

Hence: X = 5, and T = 20.

To remove duplicate solutions we can suppose the line that doesn’t include X is (B, C, D, E) and that B < E.

The following run file executes in 98ms.

Run: [ @replit ]

```#! python3 -m enigma -r

# suppose the line with no repeated digit is BCDE

SubstitutedExpression

--digits="1-9"
--distinct="BCDE"

# the 5 lines have the same sum (= 20)
"A + C + F + I = 20"
"A + D + G + J = 20"
"B + C + D + E = 20"
"B + F + H + J = 20"
"E + G + H + I = 20"

# all 9 digits are used
"len({A, B, C, D, E, F, G, H, I, J}) = 9"

# 5 is in all the lines except BCDE
"5 not in {B, C, D, E}"
"5 in {A, C, F, I}"
"5 in {A, D, G, J}"
"5 in {B, F, H, J}"
"5 in {E, G, H, I}"

# remove duplicate solutions
"B < E"

--template=""
```

Solution: The four numbers on the line without the repeated digit are: 1, 3, 7, 9.

There are 12 essentially different layouts, here is one:

Like

## Teaser 2721: Milliner’s hat-trick

A football tournament has four groups each of four teams, with the teams in the same group playing each other once. So far the teams have each played two games and in each group the distribution of points is different. Also, in each group just one pair of teams are level on points and their positions have been determined by their “goals scored”. Milliner has scored four (including a hat-trick), Orlando two, and nine other players have scored one goal each. Despite his success, Orlando’s team is not the top of their group.

What are the results of the two games that Milliner’s team have played? And the two games that Orlando’s team have played?

[teaser2721]

• #### Jim Randell 6:33 am on 19 July 2022 Permalink | Reply

The puzzle is not explicit on the scoring system used (i.e. 2 points for a win, or 3 points for a win), although it turns out it doesn’t matter which is used, the answer to the puzzle is the same in both cases.

In each group each team has played 2 of their 3 matches. So there have been 4 matches played (and there are 2 matches left to play). So among the 4 groups there have been 16 matches played in total.

The minimum possible number of goals scored among the 4 matches in a group is 2. So the maximum possible number of goals scored is 15 − 3×2 = 9. (Although it may be possible to reduce this theoretical maximum value, which would speed up the program).

For each group, this Python program considers the scores for teams A (1st), B (2nd), C (3rd), D (4th), such that each team has played 2 matches, and two of the teams are tied on points, but have different “goals for” values.

It then chooses 4 different points distributions (one for each of the groups), and from the previously examined scores it chooses 4 sets of scores that have 15 goals scored in total, and ensures there are possible teams for both Milliner and Orlando.

The program runs in 361ms.

Run: [ @replit ]

```from collections import defaultdict
from enigma import (
Football, irange, subsets, tuples, cproduct, peek, delete,
update, find, unzip, reverse, map2str, join, printf
)

# scoring regime (2 or 3 points for a win)
football = Football(games='wdlx', points=dict(w=2, d=1))

# labels for the teams
teams = (A, B, C, D) = tuple("ABCD")

# keys for the matches
ks = list(x + y for (x, y) in subsets(teams, size=2))

# allocate <t> goals among the matches <ms>, return scores <ss>
def allocate(t, ms, ss=dict()):
# are we done?
if not ms:
if t == 0:
yield ss
else:
# calculate the number of surplus goals
n = t - sum(v == 'w' for v in ms.values())
if n < 0: return
# choose the next match to allocate goals to
(k, v) = peek(ms.items())
if v == 'x':
# not played
yield from allocate(t, delete(ms, [k]), update(ss, [(k, None)]))
elif v == 'w' or v == 'l':
# a win (or loss), calculate scores (x, y)
for x in irange(1, n + 1):
for y in irange(0, min(x - 1, n - x + 1)):
s = ((x, y) if v == 'w' else (y, x))
yield from allocate(t - x - y, delete(ms, [k]), update(ss, [(k, s)]))
elif v == 'd':
# a draw, calculate scores (x, x)
for x in irange(0, n // 2):
yield from allocate(t - x - x, delete(ms, [k]), update(ss, [(k, (x, x))]))

# check goals scored by team <t> in a match from scores <ss> is <n> or more
def scored(t, ss, n):
for (k, v) in ss.items():
if v is not None:
i = find(k, t)
if i != -1 and v[i] >= n:
return True

# map: <pts> -> <total goals> -> (<match scores>, <milliner>, <orlando>) values
d = defaultdict(lambda: defaultdict(list))

# consider possible match outcomes
for ms in football.games(repeat=len(ks)):
ms = dict(zip(ks, ms))
# compute the table for each team
table = dict((t, football.extract_table(ms, t)) for t in teams)
# each team has played twice
if not all(table[t].played == 2 for t in teams): continue
# points are increasing, with exactly 2 teams having the same points
pts = tuple(table[t].points for t in teams)
if len(set(pts)) != 3: continue
if not all(x >= y for (x, y) in tuples(pts, 2)): continue
# allocate goals to the matches
for t in irange(2, 9):
for ss in allocate(t, ms):
# extract the goals (for, against) for each team
goals = dict((t, football.extract_goals(ss, t)) for t in teams)
# check teams with same points have different "goals for" values
if any(p1 == p2 and not (goals[t1][0] > goals[t2][0])
for ((t1, p1), (t2, p2)) in tuples(zip(teams, pts), 2)
): continue
# find teams for Milliner: team must have a match with >= 3 goals scored
# and have >= 4 goals scored in total
tM = list(t for (t, (f, _)) in goals.items() if f > 3 and scored(t, ss, 3))
# find teams for Orlando: must have >= 2 goals, and not be top
tO = list(t for (t, (f, _)) in goals.items() if t != 'A' and f > 1)
# record this set of scores
d[pts][t].append((ss, tM, tO))

# collect scores from <ss> for teams in <ts> (from the team's perspective)
def collect(ss, ts):
for t in ts:
rs = list()
for (s, f) in unzip(football.extract(ss, t)):
if s is None: continue
rs.append(reverse(s) if f else s)
yield tuple(sorted(rs, reverse=1))

# choose 4 different points distributions
for ps in subsets(d.keys(), size=4):
# choose the number of goals scored in each group
for ts in cproduct(d[p].keys() for p in ps):
# there should be 15 goals in total
if sum(ts) != 15: continue
# choose the actual scores in each match
for ss in cproduct(d[p][t] for (p, t) in zip(ps, ts)):
# check for possibilities for Milliner and Orlando
if not any(tM for (_, tM, _) in ss): continue
if not any(tO for (_, _, tO) in ss): continue
# output a solution (groups = scores, pts, M, O; M's team; O's team)
(Ms, Os) = (set(), set())
for (i, ((s, tM, tO), p)) in enumerate(zip(ss, ps), start=1):
printf("Group {i}: {s} {p} {tM} {tO}", s=map2str(s, enc=""), tM=join(tM, enc="[]"), tO=join(tO, enc="[]"))
Ms.update(collect(s, tM))
Os.update(collect(s, tO))
for M in sorted(Ms): printf("M's team: {M}", M=join(M, sep=", "))
for O in sorted(Os): printf("O's team: {O}", O=join(O, sep=", "))
printf()
```

Solution: The results for Milliner’s team are: a 3-1 win, a 1-0 win. The results for Orlando’s team are: a 2-0 win, a 0-1 loss.

M’s team is a group leader. O’s team is second in their group.

With “2 points for a win” (and 1 for a draw) there are three possible sets of scores:

Group 1: A v B = 1-0; A v C = 1-0; B v D = 2-0; C v D = 1-0; points = [4, 2, 2, 0], Orlando is team B
Group 2: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [4, 2, 1, 1], Milliner is team A
Group 3: A v B = 1-0; A v C = 0-0; B v D = 1-0; C v D = 0-0; points = [3, 2, 2, 1]
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [3, 3, 2, 0]

Group 1: A v B = 1-0; A v D = 1-0; B v C = 2-0; C v D = 1-0; points = [4, 2, 2, 0], Orlando is team B
Group 2: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [4, 2, 1, 1], Milliner is team A
Group 3: A v B = 1-0; A v C = 0-0; B v D = 1-0; C v D = 0-0; points = [3, 2, 2, 1]
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [3, 3, 2, 0]

Group 1: A v C = 1-0; A v D = 1-0; B v C = 0-1; B v D = 2-0; points = [4, 2, 2, 0], Orlando is team B
Group 2: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [4, 2, 1, 1], Milliner is team A
Group 3: A v B = 1-0; A v C = 0-0; B v D = 1-0; C v D = 0-0; points = [3, 2, 2, 1]
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [3, 3, 2, 0]

We can examine what happens with “3 points for a win” by changing the initialisation at line 8. And we find in this case there are four possible sets of scores:

Group 1: A v B = 1-0; A v D = 0-0; B v C = 2-0; C v D = 1-0; points = [4, 3, 3, 1], Orlando is team B
Group 2: A v B = 1-1; A v D = 1-0; B v C = 0-0; C v D = 0-0; points = [4, 2, 2, 1]
Group 3: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [6, 2, 1, 1], Milliner is team A
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [4, 4, 2, 0]

Group 1: A v B = 1-0; A v D = 0-0; B v C = 2-0; C v D = 1-0; points = [4, 3, 3, 1], Orlando is team B
Group 2: A v C = 0-0; A v D = 1-0; B v C = 0-0; B v D = 1-1; points = [4, 2, 2, 1]
Group 3: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [6, 2, 1, 1], Milliner is team A
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [4, 4, 2, 0]

Group 1: A v C = 1-0; A v D = 0-0; B v C = 0-1; B v D = 2-0; points = [4, 3, 3, 1], Orlando is team B
Group 2: A v B = 1-1; A v D = 1-0; B v C = 0-0; C v D = 0-0; points = [4, 2, 2, 1]
Group 3: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [6, 2, 1, 1], Milliner is team A
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [4, 4, 2, 0]

Group 1: A v C = 1-0; A v D = 0-0; B v C = 0-1; B v D = 2-0; points = [4, 3, 3, 1], Orlando is team B
Group 2: A v C = 0-0; A v D = 1-0; B v C = 0-0; B v D = 1-1; points = [4, 2, 2, 1]
Group 3: A v C = 3-1; A v D = 1-0; B v C = 0-0; B v D = 0-0; points = [6, 2, 1, 1], Milliner is team A
Group 4: A v C = 0-0; A v D = 2-0; B v C = 0-0; B v D = 1-0; points = [4, 4, 2, 0]

Like

• #### Jim Randell 7:29 am on 12 July 2022 Permalink | Reply Tags: by: Victor Bryant ( 86 )

My grandson and I play a coin game. First we toss seven coins and I have to predict in advance the number of heads whilst he has to predict the number of tails. I then get a number of points equal to the number of heads, he gets a number of points equal to the number of tails, and anyone whose prediction was correct gets a fixed bonus number of points (less than 40). We repeat this with six coins in the second round, then five, and so on down to two. In a recent game we noticed that, after each round, the total of all the points so far awarded was equal to a prime number.

What is the “fixed bonus” number of points? And what was the total of all the points at the end of the game?

[teaser2724]

• #### Jim Randell 7:30 am on 12 July 2022 Permalink | Reply

In a round with n coins the points awarded are, the number of heads (+ bonus if guessed correctly) and the number of tails (+ bonus if guessed correctly). So n points are always awarded, along with 0, 1, or 2 bonuses.

We don’t need to worry about the points won by each player, just the total number of points gained in each round.

This Python program keeps a set of (total, bonus) pairs, and then progresses through the rounds keeping viable values.

It runs in 57ms. (Internal runtime is 664µs).

Run: [ @replit ]

```from enigma import (irange, is_prime, printf)

# start with a total of 0, and all possible bonus values
ss = set((0, x) for x in irange(0, 40))

k = 7

# consider subsequent rounds
while k > 1:
ss_ = set()
# consider (total, bonus) in the previous round
for (t, b) in ss:
# consider number of bonuses awarded in this round
for n in (0, 1, 2):
t_ = t + k + n * b
if is_prime(t_):
# move on to the next round
(ss, k) = (ss_, k - 1)

# output solutions
for (t, b) in ss:
printf("total = {t}, bonus={b}")
```

Solution: The number of bonus points is 19. The total number of points at the end of the game is 103.

The progression of the game is:

7 coins: total = 7 (0 bonus points)
6 coins: total = 13 (0 bonus points)
5 coins: total = 37 (19 bonus points)
4 coins: total = 79 (38 bonus points)
3 coins: total = 101 (19 bonus points)
2 coins: total = 103 (0 bonus points)

Like

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r