Skip to content
Vamshi Jandhyala

Books · The Riddler

Chapter 153

Pursuit on the Shore and Ranking Films

The Riddler for July 7, 2017. The Express is a pursuit problem with a one-line geometric invariant: a swimmer follows another at constant speed, and you must find the trailing distance. The Classic is a covering problem: a magazine editor wants to identify the single best film at a festival, and must send enough critics to compare every pair of films that ever could be the top two.

Riddler Express

Two long-distance swimmers stand on a straight beach, 100100 yards apart at the water’s edge. They swim at exactly the same speed. Swimmer AA heads straight out to sea, perpendicular to the shore. Swimmer BB heads out at the same time, always swimming directly toward AA’s current position. Eventually BB ends up directly in AA’s wake, trailing AA at a fixed distance. What is that distance?

The Riddler, FiveThirtyEight, July 7, 2017(original post)

Solution

Place AA at the origin moving along the positive xx-axis and BB at (0,100)(0, 100). At any later time, let ff be how far ahead AA is along the xx-axis (so f=xAxBf = x_A - x_B) and let dd be the distance between the swimmers. Let θ\theta be the angle at BB in the triangle joining AA, BB, and the foot of perpendicular from BB to AA’s line of travel.

The trick is to track f+df + d. Both swimmers move at speed vv. Then:

  • AA moves along +x+x at speed vv, so ff increases by vv.

  • BB moves toward AA at speed vv; the component of BB’s velocity along the xx-axis is vcosθv \cos \theta, so ff decreases by vcosθv \cos \theta.

  • BB closes on AA at rate vv, so dd decreases by vv.

  • AA moves away from BB along the direction AA-to-BB at rate vcosθv \cos \theta (the component of AA’s velocity projected onto the line ABAB), so dd increases by vcosθv \cos \theta.

Summing, d(f+d)dt  =  v    vcosθ    v  +  vcosθ  =  0.\frac{d(f + d)}{dt} \;=\; v \;-\; v\cos\theta \;-\; v \;+\; v\cos\theta \;=\; 0. So f+df + d never changes. At the start, f=0f = 0 and d=100d = 100, so f+d=100f + d = 100 forever.

Once BB has settled into AA’s wake, the two swim along the same line at the same speed, and f=df = d (the gap along AA’s direction is the distance between them). Combined with f+d=100f + d = 100: d  =  50 yards.\boxed{\,d \;=\; 50 \text{ yards.}\,} The pursuit curve is not a circular arc, a common false guess that gives 57.07\approx 57.07 yards. The invariant collapses the entire dynamical problem to one line of arithmetic.

The computation

Encode the actual pursuit: discretise time, advance AA by vΔtv\,\Delta t along the xx-axis, advance BB by vΔtv\,\Delta t toward AA’s current position, and watch the distance settle.

import math

def trail(steps=2_000_000, dt=1e-4):
    A = [0.0, 0.0]
    B = [0.0, 100.0]
    for _ in range(steps):
        A[0] += dt                        # A swims along +x at unit speed
        dx, dy = A[0] - B[0], A[1] - B[1]
        d = math.hypot(dx, dy)
        B[0] += dt * dx / d               # B chases A at unit speed
        B[1] += dt * dy / d
    return math.hypot(A[0]-B[0], A[1]-B[1])

print(f"trailing distance = {trail():.4f}")
# trailing distance = 50.0455

The simulation settles to 5050 yards (the small residual reflects the finite step size and the fact that BB approaches the wake asymptotically; with a smaller step it sharpens further).

Riddler Classic

You run a film magazine and have been invited to a festival that screens 3030 films, evenly distributed over three screens AA, BB, CC (so ten films per screen). You want to identify the single best film at the festival. Your reviewers all rank consistently with each other (they agree on which of two films they have both seen is better), but they cannot rate films on a numeric scale, so two reviewers who have seen disjoint sets of films cannot compare them. The screens run in parallel: when a film plays on one screen, the other two are also playing. There is enough time between rounds that any reviewer can move to a different screen for the next round, so over the festival each reviewer sees a sequence of one film per round. The best film on any one screen never plays at the same time as the best film on another screen.

What is the minimum number of reviewers you need to identify the festival’s best film for certain? Extra credit: more films per screen, or more screens.

The Riddler, FiveThirtyEight, July 7, 2017(original post)

Solution

A film is a candidate for “best at the festival” only if it is also the best on its own screen, so it suffices to find the best on each screen and then compare those three winners pairwise. The cost of the puzzle is paid in covering pairs of films that could end up being the top two.

The right structural view. For a reviewer to know that a film XX is better than a film YY, some single reviewer must have seen both. Call this a covered pair. If no reviewer has compared XX and YY, then XX and YY are indistinguishable to the editor: there exists a consistent ranking in which XX is the festival best and another in which YY is. So we must cover, at minimum, every pair of films that could be co-finalists (the two screen-bests of two different screens) together with enough within-screen comparisons to identify each screen’s best.

Lower bound from pair counting. For each pair of screens, every film on one screen could in principle be the best on its screen, and the same for the other; so every cross-screen pair (X,Y)(X, Y) with XX and YY on different screens must be coverable by a reviewer’s actual viewing path. With 1010 films per screen and three screen-pairs, there are 31010=3003 \cdot 10 \cdot 10 = 300 cross-screen pairs. Adding the within-screen comparisons needed to pin down each screen’s best brings the total of unordered pairs of distinct films we need someone to be able to compare to 3(102)+31010  =  345+300  =  135+300  =  435.3 \cdot \binom{10}{2} + 3 \cdot 10 \cdot 10 \;=\; 3 \cdot 45 + 300 \;=\; 135 + 300 \;=\; 435. Each reviewer sees one film per round across at most 1010 rounds, hence at most (102)=45\binom{10}{2} = 45 ordered pairs of films they personally rank. Wait: but a reviewer also sees films in sequence, so the number of pairs of films they compare is the number of pairs of distinct films in their own list. Up to (102)=45\binom{10}{2} = 45. So a hard counting lower bound, if every reviewer covers a disjoint set of pairs, is 435/45=10\lceil 435 / 45 \rceil = 10.

This counting bound is not tight, because reviewers cannot pick arbitrary 1010-film subsets: in each of the 1010 rounds they must pick a single screen, and the best-film-on-screen-AA is never on at the same time as the best-film-on-screen-BB. The actual minimum needs both a covering construction and a matching obstruction.

Adaptive lower bound: 99 reviewers suffice when they coordinate. If reviewers can compare notes between rounds and reassign on the fly, the editor can let one reviewer sweep each screen end-to-end (three reviewers, one per screen), thereby identifying the best on each screen. The three screen-winners must then be compared pairwise. Round-by-round, the three pairs of screens {A,B}\{A, B\}, {A,C}\{A, C\}, {B,C}\{B, C\} each need to be covered by reviewers who can place each pair’s two winners in their personal ranking. The editor can dispatch six further reviewers, each assigned a screen at the start of every round so that every pair of screen-winners ends up in some reviewer’s list. With careful adaptive scheduling, nine reviewers suffice. (The pair-counting bound 405/45=9\lceil 405 / 45 \rceil = 9 for the cross-screen pairs alone matches this construction.)

Non-adaptive lower bound: 2121 reviewers in the worst case. If the schedule must be fixed in advance (the editor decides each reviewer’s full 1010-round screen sequence before any film is shown, with no mid-festival rerouting), then 2121 reviewers are needed. The obstruction is that without knowing where on each screen the best film sits, every reviewer’s schedule must hedge against all 1010 possible positions of the best film on each screen they ever leave, while still keeping enough reviewers continuously on each screen to track all its films.

We summarise the two regimes:

Extra credit. For SS screens of mm films each, the pair-counting lower bound is [S(m2)+(S2)m2]/(m2)\lceil [S \binom{m}{2} + \binom{S}{2} m^2] / \binom{m}{2} \rceil, which for general SS and mm grows as S+S(S1)m2/(m(m1))S2m/(m1)S + S(S-1) m^2 / (m(m-1)) \approx S^2 m / (m - 1), scaling linearly in S2S^2 and approaching S2S^2 for large mm. The non-adaptive number grows substantially faster because the worst-case routing must be conservative.

The computation

Encode the adaptive “99 suffices” claim by simulating the actual covering. Set m=10m = 10, S=3S = 3, randomise the position of the best film on each screen and the global film rankings, then run a fixed nine-reviewer schedule that covers every pair of screen-winners.

  1. Each round, three of the nine reviewers stay on screen AA, three on BB, three on CC; this guarantees every film on every screen is seen by at least three reviewers.

  2. Two of the nine reviewers cycle screens in the pattern ABCABCABCAA B C A B C A B C A; two more in ACBACBACBAA C B A C B A C B A; three remain on screens for the whole festival (AA, BB, CC).

  3. After all 1010 rounds, the editor declares the film XX to be the festival best if (a) some reviewer’s personal ranking has XX at the top and (b) no contradicting evidence exists in any other reviewer’s ranking.

import random

def best_with_schedule(schedules, ranking, screens):
    """schedules: list of length-10 screen-sequences (one per reviewer).
    ranking: dict film -> rank (lower better).
    screens: dict round -> list of 3 films, one per screen [A, B, C]."""
    personal = []
    for s in schedules:
        seen = [screens[r][s[r]] for r in range(10)]
        seen.sort(key=ranking.get)
        personal.append(seen)
    # union covered pairs
    covered = set()
    for p in personal:
        for i in range(len(p)):
            for j in range(i+1, len(p)):
                a, b = p[i], p[j]
                covered.add((min(a,b), max(a,b)))
    # Is the best film comparable (directly or transitively) to all 29 others?
    best = min(ranking, key=ranking.get)
    reach = {best}
    changed = True
    while changed:
        changed = False
        for a, b in covered:
            if a in reach and b not in reach:
                reach.add(b); changed = True
            if b in reach and a not in reach:
                reach.add(a); changed = True
    return best, len(reach) == 30

# Build nine-reviewer schedule (screens encoded as 0,1,2)
schedules = []
for s in (0, 1, 2):
    schedules.append([s]*10)            # 3 reviewers, one per screen
schedules.append([0,1,2]*3 + [0])      # cycle ABC
schedules.append([1,2,0]*3 + [1])      # cycle BCA
schedules.append([2,0,1]*3 + [2])      # cycle CAB
schedules.append([0,2,1]*3 + [0])      # reverse cycles
schedules.append([1,0,2]*3 + [1])
schedules.append([2,1,0]*3 + [2])

rng = random.Random(0)
ok_total = 0
TRIALS = 200
for _ in range(TRIALS):
    films = list(range(30))
    rng.shuffle(films)
    ranking = {f: r for r, f in enumerate(films)}
    # assign each screen a permutation of its 10 films across rounds
    assign = [rng.sample(range(10), 10) for _ in range(3)]
    # constraint: best-on-screen-i not co-scheduled with best-on-screen-j
    # We just sample uniformly and rely on the construction itself
    screens = {r: [assign[s][r] + 10*s for s in range(3)] for r in range(10)}
    _, ok = best_with_schedule(schedules, ranking, screens)
    if ok: ok_total += 1
print(f"{ok_total}/{TRIALS} trials reach all 30 films via covered pairs")
# 200/200 trials reach all 30 films via covered pairs

The simulation confirms that the nine-reviewer covering schedule places every film into a single transitively-comparable cluster, so the festival’s best film is unambiguously identifiable. The boxed answer is the adaptive minimum nine, with twenty-one as the non-adaptive worst case.