Chapter 198
To Solve The Eccentric Billionaire’s Puzzle, You Must First Defeat The Banker
Riddler Express
Abby and Beatrix roll two six-sided dice whose faces are coloured red or blue (rather than numbered). Abby wins if the top faces share a colour and Beatrix wins if they do not. One die has five blue sides and one red side. How many red and blue sides must the other die have for the game to be fair?
The Riddler, FiveThirtyEight, September 14, 2018(original post)
Solution
Let the second die have red sides and blue sides, with . Abby wins when both faces are red, or both are blue: A fair game asks : The clean reason: a perfectly balanced die’s colour is a fair coin, and it matches the other die exactly half the time regardless of how the other die is painted.
The computation
Brute-force every and exhaustively enumerate the rolls.
For each build the two dice as length- colour lists.
Iterate the rolls and tally matches.
Print for each and identify the fair value.
from fractions import Fraction
die_A = ['B'] * 5 + ['R']
for x in range(7):
die_B = ['R'] * x + ['B'] * (6 - x)
matches = sum(1 for a in die_A for b in die_B if a == b)
p = Fraction(matches, 36)
flag = " <-- fair" if p == Fraction(1, 2) else ""
print(f"x={x}: P(match) = {p} = {float(p):.4f}{flag}")
The script prints exactly at .
Riddler Classic
An eccentric billionaire has published a map and offered $10 million to anyone who can colour it with three colours so that no two bordering regions share a colour. You believe you have a valid colouring but cannot afford the $10,000 it takes to travel to her island to present it. You ask the bank for a $10,000 loan, but the manager will not lend unless he sees a working solution; and showing him the solution lets him claim the prize himself. How can you convince the bank manager that you have a solution without revealing it? Extra credit: do similar protocols exist for other types of problem?
The Riddler, FiveThirtyEight, September 14, 2018(original post)
Solution
This is the textbook setting for an interactive zero-knowledge proof. Goldreich, Micali and Wigderson presented the same construction in 1986 for exactly this puzzle: graph -colouring (the regions are graph vertices, borders are edges). The protocol is famous because -colouring is NP-complete, so by reduction the protocol extends to every problem in NP. The verifier (the banker) ends convinced that the prover (you) holds a valid colouring, but learns nothing else about the colouring itself.
The protocol. Let the map have regions and borders. Take your real colouring with three colours, and on each round:
Permute the colour labels. Pick a uniformly random permutation of the three colours and apply it to every region. (Renaming red, blue, yellow to, say, blue, yellow, red gives a different but equally valid colouring.)
Commit. Cover each region with an opaque envelope (or hash commitment, or any binding commitment scheme), then show the covered map to the banker.
Challenge. The banker chooses one border (an edge between two adjacent regions).
Reveal. Open the two envelopes the banker named. The banker checks the colours differ.
Otherwise the banker has caught you cheating: stop, no loan.
Repeat the entire round (including the colour-permutation) many times.
Why this works.
Soundness. If you do not actually have a valid colouring, at least one border has matching colours. Each round the banker independently picks a uniformly random border, so each round catches you with probability at least . After honest-banker rounds the probability you fool him is at most . For any tolerance , taking suffices to drive the cheating probability below . For a map with say borders, rounds drop the cheating probability below ; rounds drive it below .
Zero knowledge. The banker sees, in each round, the colours of one border. Because the labels were freshly permuted, each ordered border reveal is a uniformly random ordered pair of distinct colours from three options, independent of the underlying colouring. Equivalently, the banker could have simulated his own transcript using nothing but coin flips. So the protocol reveals exactly that the colouring works, and nothing about which region holds which colour.
Extra credit. Yes, and dramatically so. Goldreich, Micali and Wigderson (1986, 1991) proved that every language in NP admits a computational zero-knowledge proof, conditional on the existence of secure commitment schemes (in turn implied by the existence of one-way functions). The trick is to reduce the language to -colouring (or any other NP-complete problem) and run the protocol above. So any claim whose validity can be checked by a polynomial-time algorithm can be proven in zero knowledge: solutions to Sudoku, to SAT, to Hamiltonian cycle, to discrete log knowledge for a public key, to "this transaction is valid in a private cryptocurrency", and so on. The whole zero-knowledge SNARK industry now in commercial use rests on this foundation.
The computation
Simulate the protocol literally on a small test map. Use a random graph as the map, attach a known valid -colouring, and run many rounds of the honest protocol; then run a "cheating prover" who supplies an invalid colouring and confirm the catch probability matches the bound.
Build a -colourable random planar-like graph; produce one valid colouring and one almost-valid colouring with exactly one bad border.
Simulate rounds where the prover permutes colours and the verifier picks a random border.
For the honest prover, confirm the protocol never rejects.
For the cheating prover, estimate the catch probability over many trials and compare with .
import random
random.seed(0)
# Triangle prism graph: two triangles 0-1-2 and 3-4-5,
# plus matching edges 0-3, 1-4, 2-5. Six vertices, nine edges.
edges = [(0,1),(1,2),(0,2),(3,4),(4,5),(3,5),
(0,3),(1,4),(2,5)]
# A valid 3-colouring: vertices coloured so no edge is monochrome.
colouring = {0:0, 1:1, 2:2, 3:1, 4:2, 5:0}
# A cheating colouring: edge (0,1) is monochrome (both 0).
cheat = {0:0, 1:0, 2:2, 3:1, 4:2, 5:0}
def round_play(cmap):
perm = list(range(3)); random.shuffle(perm)
permuted = {v: perm[c] for v, c in cmap.items()}
u, v = random.choice(edges)
return permuted[u] != permuted[v] # banker accepts iff different
K = 500
honest_catches = sum(1 for _ in range(K) if not round_play(colouring))
print(f"honest, {K} rounds: caught {honest_catches} times (expect 0)")
TRIALS = 20000
caught = sum(
1
for _ in range(TRIALS)
if any(not round_play(cheat) for _ in range(K))
)
import math
expected_catch = 1 - (1 - 1 / len(edges)) ** K
print(f"cheat, {K} rounds, {TRIALS} trials: caught {caught}/{TRIALS} "
f"= {caught/TRIALS:.4f}")
print(f"theory: catch prob over {K} rounds = {expected_catch:.6f}")
The honest prover is never caught (the colouring is valid). The cheating prover is caught on essentially every trial: the bound exceeds , well past detection.