Library · The Fiddler: Solutions · Chapter 109
Can You Fail to Lose the Tennis Match?
Consider a three-set tennis match. To win the match, you must win at least two of the three sets. A set is won by winning games, and a game by winning points.
A game is won by the first player to score four points, but the winner must win by two: if both players have three points, play continues until one of them is two points ahead. A set is won by the first player to win six games. However, if both players reach five games each, they play exactly two more games. If one player wins both, that player wins the set. If they win one each, so that it is six games all, they play a tiebreak. A tiebreak is a game played to seven points instead of four, again won by two.
What is the greatest percentage of points (tiebreak points included) a player could lose in a three-set match while still winning the match?
The Fiddler, Zach Wissner-Gross, July 12, 2024(original post)
The official solution appeared in the post of July 19, 2024 and gives , agreeing with the answer below. The Extra Credit half of that post is paywalled, so the probability of about is my own.
Solution
Building a candidate. To lose many points and still win, you want to win as few sets as possible, so play all three and lose one of them. Lose that set as badly as the rules allow: six games to love, every game to love, which is points won and lost.
The two sets you win should be as close as possible, so let each go to a tiebreak. Before it, each side wins six games. Lose your six games to love again ( won, lost). Win your six games as narrowly as possible. A game cannot be won –, because of the win-by-two rule, so the narrowest wins are –, –, – and so on. Take – for now ( won, lost). Win the tiebreak – ( won, lost). Each won set then has points won and lost. The whole match has points won and lost, so the share lost is
Is that the best? Longer deuce games look tempting, since a game won – loses a larger share of its own points than a game won –. But the extra points come in pairs, one each, and a pair of points pulls the overall share toward one half. Our share is already well above one half, so the pairs can only hurt. The weighting argument below turns this into a proof that covers every possible match at once.
The proof. Write for the points you win and for the points you lose. The share lost is at most exactly when , that is, when So give every point you lose a score of and every point you win a score of , and show that the total is never positive in a match you win. (The weights and are chosen so that the candidate above scores exactly zero.) Because scores add up, it is enough to find the highest score each piece of the match can reach.
| piece | best case | highest score |
|---|---|---|
| game you win | – | |
| game you lose | – | |
| tiebreak you win | – | |
| tiebreak you lose | – |
Each line needs a word of justification. A game you win ends – with , scoring , or ends after deuce at – with , scoring . A game you lose ends – with , scoring , or – with , scoring . Tiebreaks work the same way with in place of : a win scores at most (an extended one with , lower still), and a loss at most .
Now the sets. A set you win ends – with , or –, or – after a tiebreak. Its highest scores are so every set you win scores at most . A set you lose scores at most , by losing – (each game you win in it only lowers the score, and the – and – losses score and ).
You win the match either two sets to love, scoring at most , or two sets to one, scoring at most . Either way , and the candidate reaches . So the largest share of points you can lose and still win is Equivalently, the winner can take as little as of the points.
The computation
The program searches the scorelines directly. It lists every way a game or tiebreak can end (allowing up to three extra deuce pairs), builds every possible points total for a won set and a lost set from the set rules, and combines sets into matches you win. Throughout, for each number of points won it keeps only the largest number of points lost, since no other total can give a higher share. The best match it finds loses of points.
from fractions import Fraction
def game_scores(target, extra=3):
"""(winner's points, loser's points) for a game or tiebreak, won by two."""
return ([(target, k) for k in range(target - 1)]
+ [(target + j, target - 2 + j) for j in range(1, extra + 1)])
def add(A, B):
"""Sums of a pair from A and a pair from B: for each number of points won,
keep only the most points lost (no other total can lose a bigger share)."""
best = {}
for w1, l1 in A:
for w2, l2 in B:
best[w1 + w2] = max(best.get(w1 + w2, -1), l1 + l2)
return set(best.items())
def repeat(menu, k):
out = {(0, 0)}
for _ in range(k):
out = add(out, menu)
return out
WON = game_scores(4) # games you win, as (yours, theirs)
LOST = [(l, w) for w, l in WON]
TB_WON = game_scores(7)
TB_LOST = [(l, w) for w, l in TB_WON]
def sets(you_win):
g_w, g_l = (WON, LOST) if you_win else (LOST, WON) # winner's, loser's games
tb = TB_WON if you_win else TB_LOST
out = set()
for k in range(5): # 6-0 up to 6-4
out |= add(repeat(g_w, 6), repeat(g_l, k))
out |= add(repeat(g_w, 7), repeat(g_l, 5)) # 7-5
out |= add(add(repeat(g_w, 6), repeat(g_l, 6)), tb) # 7-6 on a tiebreak
return out
won_set, lost_set = sets(True), sets(False)
matches = add(won_set, won_set) | add(add(won_set, won_set), lost_set)
w, l = max(matches, key=lambda p: Fraction(p[1], sum(p)))
print(f"won {w}, lost {l}: share lost = {Fraction(l, w + l)} = {l / (w + l):.4f}")
It prints
won 62, lost 106: share lost = 53/84 = 0.6310
Extra Credit
The two players are evenly matched: each wins any given point with probability , independently of all other points. How likely is it that one of the players loses a strict majority of the points in the match while winning the match itself?
Solution
Follow a single number through the match: the point difference , player A’s points minus player B’s. The match winner loses a strict majority of the points exactly when the winner’s difference is negative. By symmetry the answer is twice the probability that A wins the match with .
The match could in principle last for ever, because of deuce. The observation that removes the difficulty is that deuce does not matter to . Once a game reaches three points all, every further pair of points that does not end the game is one point each, and the game ends the moment someone is two ahead. So a game that goes to deuce always ends with a difference of exactly , however long it lasts. That makes the effect of each game on a finite list. Counting the orders in which the points can fall, A wins a game – with probability , – with probability and – with probability , and reaches three all with probability , after which A wins half the time with a difference of . So a game changes by , or with probabilities , and , and by , , with the same probabilities. A tiebreak is the same with seven in place of four: A wins – for with probability , and from six all () the difference ends at .
Games are independent, so the difference over a set is the sum of the differences of its games. Tracking the games score together with the running difference is now a finite calculation: from each score, each game moves to a new score and a new difference with known probability, until the set ends. The same step, with sets in place of games, carries the calculation through the match. Every probability involved is a whole number divided by a power of two, so the result is an exact fraction. Carrying it out (in the program below) gives where is the -digit whole number
Where does it come from? A set is won with a negative difference only about of the time, so in straight sets (two such sets needed) it almost never happens: those matches contribute just . Nearly all of the answer, , comes from three-set matches, where the winner’s two sets can be close while the lost set is lopsided, the same shape as the extreme match in the main problem.
The computation
There are two programs. The first carries out the exact calculation just described, with fractions throughout, and checks that the outcomes of the match add up to probability . The second ignores all of that reasoning and simply plays half a million matches point by point, deuces and all, counting how often the match winner won fewer points. Its estimate agrees with the exact value to within its sampling error.
from fractions import Fraction
from math import comb
from collections import defaultdict
half = Fraction(1, 2)
def race(target):
"""(A wins?, A's points minus B's) for a game to `target`, won by two."""
d = defaultdict(Fraction)
for k in range(target - 1): # loser ends with k points
p = comb(target - 1 + k, k) * half ** (target + k)
d[(True, target - k)] += p
d[(False, k - target)] += p
tie = comb(2 * target - 2, target - 1) * half ** (2 * target - 2)
d[(True, 2)] += tie / 2 # from level, ends at +-2
d[(False, -2)] += tie / 2
return d
GAME, TIEBREAK = race(4), race(7)
def finish(step, done):
"""Run units (games or sets) from 0-0, tracking the score and difference."""
out, state = defaultdict(Fraction), {(0, 0, 0): Fraction(1)}
while state:
nxt = defaultdict(Fraction)
for (a, b, diff), p in state.items():
for (aw, dd), q in step(a, b).items():
a2, b2 = (a + 1, b) if aw else (a, b + 1)
if (a, b) == (6, 6): # the tiebreak decided it
a2, b2 = (7, 6) if aw else (6, 7)
winner = done(a2, b2)
if winner is None:
nxt[(a2, b2, diff + dd)] += p * q
else:
out[(winner, diff + dd)] += p * q
state = nxt
return out
def set_over(a, b):
if (max(a, b) >= 6 and abs(a - b) >= 2) or max(a, b) == 7:
return a > b
return None
SET = finish(lambda a, b: TIEBREAK if (a, b) == (6, 6) else GAME, set_over)
MATCH = finish(lambda a, b: SET, lambda a, b: a > b if max(a, b) == 2 else None)
assert sum(MATCH.values()) == 1
answer = 2 * sum(p for (a_won, d), p in MATCH.items() if a_won and d < 0)
print(answer.numerator, "/ 2 **", answer.denominator.bit_length() - 1)
print(f"= {float(answer):.6f}")
It prints
366382889151794439050218364931384718435785359829 / 2 ** 162
= 0.062672
import random
def play(rng):
"""Play a match point by point; return (A won?, A's points minus B's)."""
diff = 0
def unit(target): # one game or tiebreak, won by two
nonlocal diff
a = b = 0
while max(a, b) < target or abs(a - b) < 2:
if rng.random() < 0.5:
a += 1
else:
b += 1
diff += a - b
return a > b
sets_a = sets_b = 0
while max(sets_a, sets_b) < 2:
a = b = 0
while True:
if (a, b) == (6, 6):
won = unit(7)
break
if unit(4):
a += 1
else:
b += 1
if (max(a, b) >= 6 and abs(a - b) >= 2) or max(a, b) == 7:
won = a > b
break
sets_a += won
sets_b += not won
return sets_a == 2, diff
rng = random.Random(712)
n = 500_000
hits = 0
for _ in range(n):
a_won, diff = play(rng)
hits += (diff < 0) if a_won else (diff > 0)
p = hits / n
print(f"{p:.4f} +/- {(p * (1 - p) / n) ** 0.5:.4f}")
It prints
0.0629 +/- 0.0003