Chapter 258
How Many Sets Of Cards Can You Find?
The Riddler for March 20, 2020. The Express tracks a price marked down and up by each day. The Classic is three questions about the card game SET: the largest set-free hand, the most sets a board of twelve can hold, and the chance a random twelve contain a set.
Riddler Express
A widget’s price is cut each morning and then raised from the sale price each afternoon. After days the manager is horrified to find the price more than below the original. What is the smallest ?
The Riddler, FiveThirtyEight, March 20, 2020(original post)
Solution
A morning cut multiplies the price by ; the afternoon rise multiplies by . One full day is therefore a factor not : the price loses of its value every day. After days it stands at of the original, and we want this below : Since is still above half and drops below it, the smallest is
The computation
Multiply by each day until the price falls below half.
N = 1
while 0.99**N >= 0.5:
N += 1
print(N) # 69
The price first drops below on day , as boxed.
Riddler Classic
In SET, each of cards has four attributes (number, shape, color, shading), each taking one of three values. Three cards form a “set” if, in every attribute, they are either all alike or all different. (1) What is the largest hand with no set among it? (2) What is the most sets a board of cards can contain? (3) If you draw cards at random, what is the probability they contain at least one set?
The Riddler, FiveThirtyEight, March 20, 2020(original post)
Solution
Represent each card as a point with each coordinate in , the four attributes. The “all alike or all different” rule on three cards is exactly the condition that their three values in each coordinate sum to a multiple of ; that is, three cards form a set precisely when they sum to modulo , the same as forming a line in this four-dimensional space over the integers mod . A useful consequence: any two cards lie on exactly one line, so they determine a unique third card completing a set. Hence the deck holds sets in all.
(1) Largest set-free hand. A set-free hand is a cap: a set of points containing no line. The maximum cap in this four-dimensional space has size a fact first proved by Pellegrino in and genuinely hard (it rests on real combinatorial-geometry machinery, not case-checking, which is why we cite rather than re-derive it). The computation below exhibits a concrete -card hand with no set, confirming is attainable; that it cannot be beaten is the cited theorem.
(2) Most sets in twelve cards. A board of can be arranged to contain at most sets, found by local search over boards.
(3) Chance a random twelve contain a set. Drawing of the cards uniformly at random, the probability of at least one set is estimated by Monte Carlo. A random hand of twelve almost always contains a set; genuinely set-free hands (caps) are rare.
The computation
Cards are the points of ; three form a set when they sum to zero mod . Count all sets (); verify a specific -card hand is set-free; hill-climb for the most sets in a board of (); and Monte-Carlo the chance a random twelve contain a set ().
import itertools, random
cards = list(itertools.product(range(3), repeat=4))
def is_set(a, b, c): return all((a[i]+b[i]+c[i]) % 3 == 0 for i in range(4))
def count_sets(board):
return sum(1 for t in itertools.combinations(board, 3) if is_set(*t))
print(count_sets(cards)) # 1080 total sets in the deck
cap = [(1,1,1,1),(0,1,2,0),(1,0,0,1),(0,2,0,0),(1,2,1,1),(1,2,0,2),(1,1,0,2),
(0,0,0,2),(2,2,1,0),(2,1,1,2),(1,0,1,2),(2,2,2,1),(0,1,0,2),(2,0,2,1),
(2,0,1,0),(2,1,0,0),(0,0,2,0),(2,1,2,2),(2,1,0,1),(0,2,2,2)]
print(len(cap), count_sets(cap)) # 20 0 -> a maximal set-free hand
def max_sets_12(restarts=60, seed=1):
rng = random.Random(seed); best = 0
for _ in range(restarts):
board = rng.sample(cards, 12); cur = count_sets(board); moved = True
while moved:
moved = False
for i in range(12):
for c in cards:
if c in board: continue
nb = board[:]; nb[i] = c; v = count_sets(nb)
if v > cur: board, cur, moved = nb, v, True
best = max(best, cur)
return best
print(max_sets_12()) # 14
def prob_set(trials=100000, seed=2):
rng = random.Random(seed)
return sum(count_sets(rng.sample(cards, 12)) > 0 for _ in range(trials)) / trials
print(round(prob_set(), 3)) # ~0.968
The deck holds sets; the listed -card hand has none; the most sets in a board of is ; and a random twelve contain a set about of the time, all as boxed.