# Enigmatic Code

Programming Enigma Puzzles

## Enigma 1121: Families

From New Scientist #2277, 10th February 2001 [link]

There are six families, each consisting of mother, father and child. The mothers are Amber, Barbara, Christine, Dorothy, Ellen and Frances; the fathers are George, Harry, Inderjit, James, Kenneth and Lewis; the children are Matthew, Naomi, Oliver, Peter, Quentin and Rachel. The other day, everyone kept a diary of who they met in the 24-hour period; of course, everyone met the other two members of their family, but they also met other people. These are the diary records, given by initials:

A met G, J, L, M, N, P;
B met H, J, K, O, P, R;
C met I, K, L, M, O, Q;
D met G, I, L, P, Q, R;
E met H, I, K, M, N, R;
F met G, H, J, N, O, Q.

Also:

G met M, N, P;
H met O, P, R;
I met M, O, Q;
J met N, O, Q;
K met M, N, R;
L met P, Q, R.

If I told you who the wife of Inderjit is, then you could not work out who the father of Oliver is.

Question 1: Who is the wife of Inderjit?

If I told you who the mothers of Oliver and Quentin are, then you could work out who the mother of Peter is.

Question 2: Who are the mothers of Oliver, Quentin and Peter?

[enigma1121]

### 2 responses to “Enigma 1121: Families”

1. Jim Randell 3 April 2017 at 8:24 am

This Python program make use of the filter_unique() function from the enigma.py library. It runs in 78ms

```from itertools import permutations
from enigma import filter_unique, join, printf

mothers = 'ABCDEF'
fathers = 'GHIJKL'
kids = 'MNOPQR'

met1 = {
'A': 'GJLMNP',
'B': 'HJKOPR',
'C': 'IKLMOQ',
'D': 'GILPQR',
'E': 'HIKMNR',
'F': 'GHJNOQ',
}

met2 = {
'G': 'MNP',
'H': 'OPR',
'I': 'MOQ',
'J': 'NOQ',
'K': 'MNR',
'L': 'PQR',
}

r = list()

# choose pairings of father and mother
for fs in permutations(fathers):
# check each pair met
if not all(f in met1[m] for (m, f) in zip(mothers, fs)): continue

# and assign the children
for ks in permutations(kids):
# check both the mother and father met their child
if not all(k in met1[m] and k in met2[f] for (m, f, k) in zip(mothers, fs, ks)): continue

# record possible orderings for fathers and children
r.append((fs, ks))

# the wife of I is...
fn1a = lambda (fs, ks): mothers[fs.index('I')]
# father of O is...
fn1b = lambda (fs, ks): fs[ks.index('O')]

# find solutions where knowing the wife of I does not give the father of O
(_, q1) = filter_unique(r, fn1a, fn1b)

# output the families
families = lambda ms, fs, ks: join(map(join, zip(mothers, fs, ks)), sep=", ")

printf("[question 1]")
for (fs, ks) in q1:
wI = fn1a((fs, ks))
printf("wife of I = {wI} [{s}]", s=families(mothers, fs, ks))
printf()

# the mothers of O and Q ...
fn2a = lambda (fs, ks): (mothers[ks.index('O')], mothers[ks.index('Q')])
# the mother of P...
fn2b = lambda (fs, ks): mothers[ks.index('P')]

# find solutions where knowing the mothers of O and Q does give the mother of P
(q2, _) = filter_unique(q1, fn2a, fn2b)

printf("[question 2]")
for (fs, ks) in q2:
((mO, mQ), mP) = (fn2a((fs, ks)), fn2b((fs, ks)))
printf("mother of O = {mO}, mother of Q = {mQ}, mother of P = {mP} [{s}]", s=families(mothers, fs, ks))
printf()
```

Solution: 1. Christine is the wife of Inderjit; 2. Barbara is the mother of Oliver, Dorothy is the mother of Quentin, Amber is the mother of Peter.

If we use all the information provided we can fully determine the families:

Amber & George, Peter
Barbara & Harry, Oliver
Christine & Inderjit, Matthew
Dorothy & Lewis, Quentin
Ellen & Kenneth, Rachel
Frances & James, Naomi

For the first part there are 17 possible family groupings. (The pairings of mothers and fathers are the same in all cases).

For the second part I assumed the possible family groupings had already been restricted by the information given for the first part, which gives the unique groupings given above. But if we treat it as a question independent of the information given in question 1 then we get the same answer, but there are two possible groupings that achieve it.

We can adjust the program to solve this variant by passing r to filter_unique() in line 65 instead of q1.

Another interpretation of the problem is if in the second part we are told the mothers of O and Q, but not told which child belongs to which parent. We can adjust the return value of fn2a in line 60 to return an unordered pair. If we do this we find there are no additional solutions.

The program is written for Python 2, but can easily be adapted to run under either Python 2 or Python 3 by using the unpack() function (provided in the enigma.py library) in the definition of the functions in lines 42, 44, 60, 62.

2. Brian Gladman 4 April 2017 at 10:58 am
```from itertools import permutations
from collections import defaultdict

mothers, fathers, children = 'ABCDEF', 'GHIJKL', 'MNOPQR'

meet1 = { 'A': 'GJLMNP', 'B': 'HJKOPR', 'C': 'IKLMOQ',
'D': 'GILPQR', 'E': 'HIKMNR', 'F': 'GHJNOQ' }

meet2 = { 'G': 'MNP', 'H': 'OPR', 'I': 'MOQ',
'J': 'NOQ', 'K': 'MNR', 'L': 'PQR' }

# compile a list of possible sets of six families
sol = []
# permute the fathers who partner with the mothers
for p in permutations(fathers):
# ... who must all have met their partners
if all(f in meet1[m] for m, f in zip(mothers, p)):

# permute the children who are with each mother and father
for q in permutations(children):
# ... who must all have met both their parents
if all(c in meet1[m] for m, c in zip(mothers, q)):
if all(c in meet2[f] for f, c in zip(p, q)):

# store solutions in a list of lists of the six families
sol.append(list(m + f + c for m, f, c in zip(mothers, p, q)))

print(f'There are {len(sol)} possible family arrangements.')

# maps for mother to father and mothers to mother
m2f, mm2m = defaultdict(set), defaultdict(set)
# for each possible set of six families
for f8 in sol:
m_oq = ''
for mfc in f8:
# if I is in a family, store his partner
if 'I' in mfc:
w_i = mfc[0]
# if O is in a family, store his mother and father
if 'O' in mfc:
f_o = mfc[1]
m_oq += mfc[0]
# if Q is in a family, store his mother
if 'Q' in mfc:
m_oq += mfc[0]
# if P is in a family, store his mother
if 'P' in mfc:
m_p = mfc[0]
# map I's patner to possible fathers for O
# map combinations of O and Q's mothers to
# Peter's possible mothers

# find situations in which knowing I's partner
# does not reveal O's father
for m, ff in m2f.items():
if len(ff) > 1:
print(f"I's wife is {m}", end=', ')

# find situations in which knowing O and Q's
# mothers reveals P's mother
for m_oq, m in mm2m.items():
if len(m) == 1:
print("P's mother is {}.".format(*m))
```

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