Chapter 76
Can You Navigate The One-Way Streets?
Riddler Express
Two cups start with equal water. Repeatedly pour half of cup A into B, then half of B back into A, many times. What fraction of the water ends up in A? Extra credit: what if the cups start with arbitrary (non-zero) amounts?
Solution
Normalise the total to and let be the fraction in A. One full round does two pours. First half of A goes to B: A holds , B holds . Then half of B returns: B keeps and A gains the rest, so At a steady state this equals ; with , The map contracts toward this fixed point regardless of the starting split, so the cups always settle at in A and in B, whatever the (non-zero) initial amounts.
The computation
Iterate the two pours many times from different starting amounts and read off the fraction in A.
def fraction_A(a, b, rounds=100):
for _ in range(rounds):
a /= 2; b += a; b /= 2; a += b
return a / (a + b)
print(fraction_A(8, 8), fraction_A(3, 5)) # 0.6667, 0.6667
Riddler Classic
A block of city streets is a grid of corners joined by blocks; each block is given a random one-way direction. You commute from the top-left corner to the bottom-right. What is the probability a route still exists? And the probability of a round trip?
Solution
Number the corners through across the grid; the blocks each get one of two directions, so there are equally likely street layouts. The question is purely combinatorial: in how many of them is corner reachable from corner . Checking every layout for a directed path, exactly work, so Requiring a path back as well (a round trip, and ) is far more demanding; only layouts manage it,
The computation
Enumerate all orientations of the twelve blocks, build the directed graph for each, and count those with a path from start to work (and back).
from itertools import product
import networkx as nx
edges = [(1,2),(2,3),(1,4),(2,5),(3,6),(4,5),
(5,6),(4,7),(7,8),(5,8),(6,9),(8,9)]
go = back = 0
for dirs in product((0, 1), repeat=12):
g = nx.DiGraph()
for (u, v), d in zip(edges, dirs):
g.add_edge(u, v) if d == 0 else g.add_edge(v, u)
if nx.has_path(g, 1, 9):
go += 1
if nx.has_path(g, 9, 1): back += 1
print(go, back) # 1135, 170 (of 4096)