Chapter 59
Who Betrayed Dune’s Duke Leto?
Riddler Express
Exactly two of Jessica, Wellington, Gurney and Duncan are traitors. Traitors lie; the loyal tell the truth. Jessica says “I am not the traitor”; Wellington says the same of himself; Gurney says “Jessica or Wellington is a traitor”; Duncan says “Jessica or Gurney is a traitor” (where “or” allows both). One traitor’s name can be deduced. Who?
Solution
The two “I am not the traitor” claims carry no information: a loyal speaker truthfully denies it, and a traitor lies about it, so either way the statement is consistent. The work is done by Gurney and Duncan.
Suppose Gurney were a traitor. Then his statement is false, so neither Jessica nor Wellington is a traitor; the two traitors would have to be Gurney and Duncan. But then Duncan is also a traitor, making his statement false, so neither Jessica nor Gurney is a traitor, contradicting that Gurney is one. So Gurney is loyal, and his true statement forces at least one of Jessica, Wellington to be a traitor.
Now split on Duncan. If Duncan is loyal, his statement is true, so Jessica or Gurney is a traitor; since Gurney is loyal, Jessica is a traitor, and the second traitor (not Gurney, not Duncan) must be Wellington. If Duncan is a traitor, his statement is false, so neither Jessica nor Gurney is a traitor; the two traitors are then Duncan and, to satisfy Gurney’s statement, Wellington. Both cases share one name:
The computation
Try every pair of traitors, mark each speaker as liar or truth-teller accordingly, and keep the assignments where all four statements hold.
from itertools import combinations
who = ['J', 'W', 'G', 'D']
sols = []
for traitors in combinations(who, 2):
t = {p: (p in traitors) for p in who}
says = { # what each speaker claims
'J': not t['J'], 'W': not t['W'],
'G': t['J'] or t['W'], 'D': t['J'] or t['G']}
# traitor's claim must be false, loyal's claim must be true
if all(says[p] != t[p] for p in who):
sols.append(traitors)
common = set(who).intersection(*[set(s) for s in sols])
print(sols, common) # [('J','W'),('W','D')], {'W'}
Riddler Classic
On an equilateral triangle, pick one point uniformly at random on each edge and join them into a smaller triangle. What is the probability that the centre (centroid) of the original triangle lies inside this smaller one?
Solution
The smaller triangle is the original with three corner triangles trimmed off, one at each vertex. The centre lies in the small triangle exactly when it lands in none of those corners, and since the corners are disjoint, by the threefold symmetry of the construction Use as origin with the two edges from as axes; the random points on those edges are and with uniform on , and the centroid sits at . It lies in the corner triangle precisely when That region needs , and integrating its area, Therefore That is : a simple-looking question with a logarithm for an answer, so the tidy rational one expects never appears.
The computation
Run the experiment: drop a uniform point on each edge, build the small triangle, and test whether the centroid is inside it.
import numpy as np
rng = np.random.default_rng(0)
A = np.array([-0.5, -np.sqrt(3)/6]); B = np.array([0.5, -np.sqrt(3)/6])
C = np.array([0.0, np.sqrt(3)/3]); G = np.array([0.0, 0.0])
N = 20_000_000
x, y, z = rng.random(N), rng.random(N), rng.random(N)
D = A + np.outer(x, B - A)
E = B + np.outer(y, C - B)
Fp = C + np.outer(z, A - C)
cr = lambda P, Q: P[:, 0]*Q[:, 1] - P[:, 1]*Q[:, 0]
s1, s2, s3 = cr(G - D, E - D), cr(G - E, Fp - E), cr(G - Fp, D - Fp)
neg = (s1 < 0) | (s2 < 0) | (s3 < 0)
pos = (s1 > 0) | (s2 > 0) | (s3 > 0)
inside = ~(neg & pos)
print(inside.mean(), 2/3*np.log(2)) # ~0.462, 0.4621