Parts A and B: a formula that works mod cannot hide the remainder
With , any eight multiples of 64, or any eight tickets with the same remainder,
share a hook. Picking and at random does not help if the formula still works mod :
, so cancels, and two tickets 256 apart are
together for every . The pranksters never needed to know tonight's choice.
Part C: why exactly 3/31
Fix two different tickets . For , the map
is a bijection onto the ordered pairs of different values: given and , solve
and . So each of the choices of
gives a different pair , and the tickets collide exactly when
. Among , seven remainders mod 8 occur four times and one (7) occurs three
times. The colliding ordered pairs number , and
, below . It is the same for every pair of tickets.
Allowing breaks the bijection. Then ignores the ticket on of the nights, and the
worst pair's chance rises to , just above .
Part D: work mod a prime, reduce mod at the end
Take a prime above every ticket ( works), draw from to and
from to with the night's seed, and return . The
argument of part C shows that any two tickets collide with probability at most about ,
whatever tickets the adversary picks. The argument needs the two tickets to differ mod ,
which is why must exceed every ticket. The prime is one short: ticket
is mod , so it shares a hook with ticket every night. The grader tries
that pair. Passing part D is evidence; this argument is the proof. In the grader's run, the worst pair it tried shared a hook
on 44 of 400 nights at : close to , far below the 100 allowed. Multiply-shift
( odd and random, keep the top bits of ) is cheaper and guarantees .
The broken families fail at once: with x % m, (a * x) % m or (a * x + b) % m, tickets 0 and
16 share a hook on all 400 nights at .
Going further
- Hash flooding is a real attack. Crosby and Wallach showed in 2003 that web servers could be
slowed to a crawl by keys chosen to collide. In 2011 Klink and Wälde demonstrated it against
the form parsing of most web frameworks. Languages then randomized their string hashing:
Python uses SipHash for strings since version 3.4 (PEP 456). Small integers still hash to
themselves.
- Stronger families. Universal hashing bounds pairs. Some algorithms, such as linear probing,
need more independence. Simple tabulation hashing (Pătrașcu and Thorup) is fast and behaves
far better than its independence suggests.
- Part E points at the limit of the model: universal hashing assumes the adversary never sees
the outcome. Timing and observed collisions leak the draw, which is why keyed functions such as
SipHash are used instead.
References
- J. L. Carter, M. N. Wegman. Universal classes of hash functions. Journal of Computer and
System Sciences 18(2), 1979.
- M. Dietzfelbinger, T. Hagerup, J. Katajainen, M. Penttonen. A reliable randomized algorithm for
the closest-pair problem. Journal of Algorithms 25(1), 1997.
- S. A. Crosby, D. S. Wallach. Denial of service via algorithmic complexity attacks. USENIX
Security Symposium, 2003.
- J.-P. Aumasson, D. J. Bernstein. SipHash: a fast short-input PRF. INDOCRYPT, 2012.
- M. Pătrașcu, M. Thorup. The power of simple tabulation hashing. Journal of the ACM 59(3), 2012.