Teaser 3122: Bank robbery
From The Sunday Times, 24th July 2022 [link] [link]
Five witnesses were interviewed following a robbery at the bank in the High Street. Each was asked to give a description of the robber and his actions.
The details given were: height; hair colour; eye colour; weapon carried; escape method.
Witness 1: short; fair; brown; cricket bat; motorbike.
Witness 2: tall; fair; grey; gun; car.
Witness 3: tall; dark; brown; crowbar; motorbike.
Witness 4: short; ginger; blue; knife; car.
Witness 5: tall; dark; blue; stick; pushbike.
When the police caught up with the perpetrator, they found that each of the five witnesses had been correct in exactly two of these characteristics.
What was the robber carrying, and how did he get away?
[teaser3122]










Jim Randell 4:15 pm on 22 July 2022 Permalink |
It is straightforward to try all possible combinations. (Assuming the robber has a unique single value for each characteristic).
I include an “other” value for each characteristic to account for possibilities where none of the witnesses have correctly described it.
This Python program runs in 58ms. (Internal runtime is 1.9ms).
Run: [ @replit ]
from enigma import (cproduct, join, printf) # descriptions descs = [ dict(S='short', T='tall', X='other'), dict(F='fair', D='dark', G='ginger', X='other'), dict(R='brown', G='grey', L='blue', X='other'), dict(B='cricket bat', G='gun', C='crowbar', K='knife', S='stick', X='other'), dict(M='motorbike', C='car', P='pushbike', X='other'), ] # statements statements = [ 'SFRBM', 'TFGGC', 'TDRCM', 'SGLKC', 'TDLSP' ] # how many characteristics are correct? correct = lambda xs, ys: sum(x == y for (x, y) in zip(xs, ys)) # consider characteristics of the perp for cs in cproduct(d.keys() for d in descs): # each witness gave 2 correct statements if all(correct(xs, cs) == 2 for xs in statements): # output solution printf("{cs}", cs=join((d[k] for (d, k) in zip(descs, cs)), sep=", "))Solution: The robber was carrying a knife. He made his escape by motorbike.
In fact we can determine a complete description:
LikeLike
Jim Randell 8:48 am on 26 July 2022 Permalink |
And here is a solution using the [[
SubstitutedExpression]] solver from the enigma.py library.The following run file executes in 72ms.
Run: [ @replit ]
This construction also leads to a simple manual solution:
In the matrix of statements (lines 25 – 29), each row sums to 2. So the total sum of all matrix elements is 10.
Looking at the columns of the matrix we get the following potential column totals:
A grand total of 10 can only be achieved by taking the maximum value for each column.
So we can eliminate all the X’s and A, E, G, Q (all of which must = 0). Hence B = 1.
One of C, D = 1 (and the other = 0). If D = 1, then witnesses 3 and 5 have achieved their 2 correct statements so: F, H, K, M, N = 0, but one of F, H = 1. So D = 0 and C = 1.
We can then complete the assignment of values, and determine the true statements are: B, C, H, L, N.
LikeLike
GeoffR 5:51 pm on 23 July 2022 Permalink |
# List of possible witness statements statements = [] # Witness statements W1 = ['short', 'fair', 'brown', 'cricket bat', 'motorbike'] W2 = ['tall', 'fair', 'grey', 'gun', 'car' ] W3 = ['tall', 'dark', 'brown', 'crowbar', 'motorbike' ] W4 = ['short', 'ginger', 'blue', 'knife', 'car' ] W5 = ['tall', 'dark', 'blue', 'stick', 'pushbike' ] # Form lists of all possible witness statements for a in ('short', 'tall'): for b in ('fair', 'dark', 'ginger'): for c in ('brown', 'grey', 'blue'): for d in ('cricket bat', 'gun', 'crowbar', 'knife', 'stick'): for e in ('motorbike', 'car', 'pushbike'): statements.append([a, b, c, d, e]) for st in statements: a, b, c, d, e = st # Two statements from five are true for each witness # test Witness No.1 statements if sum([a == 'short', b == 'fair', c == 'brown', \ d == 'cricket bat', e == 'motorbike']) == 2: #test Witness No.2 statements if sum([a == 'tall', b == 'fair', c == 'grey', \ d == 'gun', e == 'car']) == 2: # test Witness No.3 statements if sum([ a == 'tall', b == 'dark', c == 'brown', \ d == 'crowbar', e == 'motorbike']) == 2: # test Witness No.4 statements if sum([a == 'short', b == 'ginger', c == 'blue', \ d == 'knife', e == 'car']) == 2: # test Witness No.5 statements if sum([ a == 'tall', b == 'dark', c == 'blue', \ d == 'stick', e == 'pushbike']) == 2: print(f"The robber was {a}.") print(f"He had {b} coloured hair and {c} colour eyes.") print(f"He carried a {d} as a weapon, escaping on a {e}.")LikeLike
GeoffR 9:41 pm on 23 July 2022 Permalink |
Indexing the witness statement lists is neater:
# List of possible witness statements statements = [] # Witness statements W1 = ['short', 'fair', 'brown', 'cricket bat', 'motorbike'] W2 = ['tall', 'fair', 'grey', 'gun', 'car' ] W3 = ['tall', 'dark', 'brown', 'crowbar', 'motorbike' ] W4 = ['short', 'ginger', 'blue', 'knife', 'car' ] W5 = ['tall', 'dark', 'blue', 'stick', 'pushbike' ] # Form lists of all possible witness statements for a in ('short', 'tall'): for b in ('fair', 'dark', 'ginger'): for c in ('brown', 'grey', 'blue'): for d in ('cricket bat', 'gun', 'crowbar', 'knife', 'stick'): for e in ('motorbike', 'car', 'pushbike'): statements.append([a, b, c, d, e]) for st in statements: a, b, c, d, e = st # Two statements from five are true for each witness # test Witness No.1 statements if sum([a == W1[0], b == W1[1], c == W1[2], \ d == W1[3], e == W1[4]]) == 2: # test Witness No.2 statements if sum([a == W2[0], b == W2[1], c == W2[2], \ d == W2[3], e == W2[4]]) == 2: # test Witness No.3 statements if sum([ a == W3[0], b == W3[1], c == W3[2], \ d == W3[3], e == W3[4]]) == 2: # test Witness No.4 statements if sum([a == W4[0], b == W4[1], c == W4[2], \ d == W4[3], e == W4[4]]) == 2: # test Witness No.5 statements if sum([ a == W5[0], b == W5[1], c == W5[2], \ d == W5[3], e == W5[4]]) == 2: print(f"The robber was {a}.") print(f"He had {b} coloured hair and {c} colour eyes.") print(f"He carried a {d} as a weapon, escaping on a {e}.")LikeLike
GeoffR 12:07 pm on 1 August 2022 Permalink |
% A Solution in MiniZinc include "globals.mzn"; set of int: TF = {1,0}; var TF:short; var TF:tall; var TF:h_fair; var TF:h_dark; var TF:h_ginger; var TF:e_brown; var TF:e_grey; var TF:e_blue; var TF:c_bat; var TF:gun; var TF:c_bar; var TF:knife; var TF:stick; var TF:mbike; var TF:car; var TF:pbike; % Values are 0 or 1 for main variables % ..i.e. for height, hair colour, eye colour, weapon, vehicle constraint sum([short, tall]) < 2; constraint sum([h_fair, h_dark, h_ginger]) < 2; constraint sum([e_brown, e_grey, e_blue]) < 2; constraint sum([c_bat, gun, c_bar, knife, stick]) < 2; constraint sum([mbike, car, pbike]) < 2; % 5 witness statements - 2 are true for each witness constraint sum([short, h_fair, e_brown,c_bat, mbike]) == 2; constraint sum([tall, h_fair, e_grey, gun, car]) == 2; constraint sum([tall, h_dark, e_brown, c_bar, mbike]) == 2; constraint sum([short, h_ginger, e_blue, knife, car]) == 2; constraint sum([tall, h_dark, e_blue, stick, pbike]) == 2; solve satisfy; output [" [short, tall ] = " ++ show([ short, tall ]) ++ "\n" ++ " [h_fair, h_dark, h_ginger] = " ++ show([ h_fair, h_dark, h_ginger]) ++ "\n" ++ " [e_brown, e_grey, e_blue] = " ++ show([e_brown, e_grey, e_blue] ) ++ "\n" ++ " [c_bat, gun, c_bar, knife, stick] = " ++ show([c_bat, gun, c_bar, knife, stick]) ++ "\n" ++ " [mbike, car, pbike] = " ++ show([mbike, car, pbike]) ]; % [short, tall ] = [0, 1] % [h_fair, h_dark, h_ginger] = [1, 0, 0] % [e_brown, e_grey, e_blue] = [0, 0, 1] % [c_bat, gun, c_bar, knife, stick] = [0, 0, 0, 1, 0] % [mbike, car, pbike] = [1, 0, 0] % ---------- % ========== % i.e. Robber was tall, had fair hair, blue eyes, with a knife, escaping on a motorbike.LikeLike