Chapter 158
Can You Plug The White House Leak?
Riddler Express
You are the president, and exactly one of your staffers is a leaker. You can fabricate distinct stories and give each staffer a different subset; any staffer with a story will leak it. What is the smallest number of distinct stories you need to identify the leaker for sure?
The Riddler, FiveThirtyEight, August 11, 2017(original post)
Solution
Encode the identity of the leaker as a binary string: each story is one bit, with the bit equal to if the leaker was given that story and otherwise. The president picks a labelling of the staffers by distinct binary strings, then assigns each story to the subset of staffers whose bit-string has a in that position. After watching which stories leak, the president reads off the bit-string and recovers the leaker.
The number of distinguishable bit-strings on stories is , so we need , that is The smallest integer satisfying this is , giving distinguishable codes.
The classical phrasing of this construction is a sequential binary search: split the staff in half, give one story to the first , see whether it leaks, then split the surviving in half, and so on. Worst-case the suspect set shrinks , which is again seven steps; the cleanest description is the simultaneous one above (no waiting between stories), but both reach the same .
The lower bound is information-theoretic: each story is a yes/no observation, so stories produce only possible outcome vectors. To distinguish candidate leakers the president must have , that is . Seven is therefore both achievable and necessary.
The computation
Encode the problem as a binary-labelling enumeration: for any number of stories , the president identifies the leaker if and only if the labelling assigns distinct binary strings to the staffers. The smallest for which this is possible is the answer.
import math
N = 100
k = math.ceil(math.log2(N))
print(f"stories needed for {N} staffers = {k}") # 7
# explicit binary labelling: staffer i gets the binary string of i,
# story j is given to all staffers whose j-th bit is 1.
labels = [format(i, f"0{k}b") for i in range(N)]
print(len(set(labels)) == N) # True (all distinct)
# minimal k is forced: 2**(k-1) < N
print(2 ** (k - 1) < N <= 2 ** k) # True
The labelling assigns distinct binary strings, and confirms is the smallest feasible value.
Riddler Classic
Five regular tetrahedral dice are placed around a common edge, each sharing a triangular face with its neighbours. They almost close up into a closed pentagonal ring, but a small angular gap remains. What is the size of that gap?
The Riddler, FiveThirtyEight, August 11, 2017(original post)
Solution
The gap is minus five copies of the regular tetrahedron’s dihedral angle.
The dihedral angle of a regular tetrahedron (the angle between any two of its faces meeting along an edge) is Take a tetrahedron with side and look at it across one of its edges. The two adjacent faces meet at that edge; their inward-pointing perpendiculars from the edge to the opposite vertices have length equal to the height of an equilateral triangle of side , that is . The vertex-to-vertex distance across the dihedral is the edge length of the opposite edge, which is also . Apply the law of cosines to the isoceles triangle with two sides and apex angle : giving Now stack five tetrahedra around a common edge. Each contributes a dihedral angle to the total angle swept around that edge, and the gap is whatever is left of : Numerically,
The five tetrahedra cannot close into a ring: the dihedral is irrational in degrees and not a divisor of , so no finite number of identical regular tetrahedra can perfectly tile a ring around a common edge. The gap is intrinsic to the geometry, not a measurement error.
The computation
Encode the problem with explicit unit vectors for the two faces meeting at the shared edge of a single regular tetrahedron, compute the dihedral angle from their normals, and multiply by five. As a separate check, build the five tetrahedra explicitly in D and measure the wedge angle that remains.
Place one regular tetrahedron with vertices at known coordinates; pick an edge and identify the two adjacent faces.
Compute the outward normals of those two faces; the dihedral is the angle between the inward normals.
Multiply by and subtract from .
import numpy as np
v = np.array([
[1, 1, 1],
[1, -1, -1],
[-1, 1, -1],
[-1, -1, 1],
], dtype=float)
centroid = v.mean(axis=0)
def outward_normal(a, b, c, inside):
n = np.cross(b - a, c - a)
n /= np.linalg.norm(n)
if np.dot(n, (a + b + c) / 3 - inside) < 0:
n = -n
return n
# edge: v[0]-v[1]. Faces sharing it: (0,1,2) and (0,1,3).
n1 = outward_normal(v[0], v[1], v[2], centroid)
n2 = outward_normal(v[0], v[1], v[3], centroid)
# dihedral = pi minus angle between outward normals
gamma = np.pi - np.arccos(np.dot(n1, n2))
print(round(np.degrees(gamma), 4)) # 70.5288
gap = 360 - 5 * np.degrees(gamma)
print(round(gap, 4)) # 7.3561
The dihedral angle prints as (matching ), and the gap prints as , in agreement with the boxed value.