Coding Interview Patterns Module 18 – Maths for coding rounds
Random shuffles and sampling
The Fisher–Yates shuffle, reservoir sampling from a stream and weighted random picks, with seeded tests and the shortcuts that bias the result.
What you will learn
- Implement a uniform shuffle with Fisher–Yates and prove that every order is equally likely
- Select k items from a stream of unknown length with reservoir sampling
- Pick indexes by weight with running totals and binary search, never picking a zero weight
- Build a uniform 1 to 10 from a uniform 1 to 7 with rejection sampling, and avoid modulo bias
- Test random code reproducibly with seeds in Python, JavaScript, Java, C++ and Go
Before you start
What you’ll learn · Before you start
What you will learn
- Implement a uniform shuffle with Fisher–Yates and prove that every order is equally likely
- Select k items from a stream of unknown length with reservoir sampling
- Pick indexes by weight with running totals and binary search, never picking a zero weight
- Build a uniform 1 to 10 from a uniform 1 to 7 with rejection sampling, and avoid modulo bias
- Test random code reproducibly with seeds in Python, JavaScript, Java, C++ and Go
Before you start
On this page
The Fisher–Yates shuffle puts n items in random order so that each of the n! orders is equally likely: walk from the last position down to the second, and swap the item there with one at a uniformly random position at or before it. Random shuffles, samples and weighted picks come up in interviews because the obvious code is often quietly unfair. Sampling has two more classics: reservoir sampling keeps k items from a stream whose length nobody knows, and a weighted pick returns index i with probability proportional to its weight. Rejection sampling turns one fair generator into another by discarding the draws that would make the result uneven. Two popular shortcuts, sorting with a random comparator and swapping each item with any position at all, give random shuffles that are measurably biased.
Three draws for a college fest
Problem A: shuffle the entries. A college fest’s lucky draw lists n ≤ 10⁶ registration numbers. Using
randbelow(m), which returns a uniform integer from 0 to m − 1, put the list in random order in place, so that each
of the n! orders has the same chance.
Problem B: sampling a stream. Tickets are scanned at the gate one by one, and nobody knows how many will come. Keep k = 5 of them in memory so that, when the gate closes, every scanned ticket is among the five with the same probability.
Problem C: pick a stall by weight. Stall i holds wᵢ prize tokens, 0 ≤ wᵢ ≤ 10⁹, for n ≤ 10⁵ stalls. Build a picker once, then answer 10⁵ picks, each returning stall i with probability wᵢ / W, where W is the total. With the weights 1, 3, 0 and 6, stall 3 wins 60 % of the picks and stall 2 never wins.
A shuffle step by step, and a weighted pick as a point on a line
Text description of the diagram
The upper panel shuffles the four finalists A, B, C and D. At i = 3 a random position j from 0 to 3 is chosen, here 1, and B and D swap, so B is final in the last position: A, D, C, B. At i = 2, j is chosen from 0 to 2, here 2, so C stays and is final. At i = 1, j is chosen from 0 to 1, here 0, and A and D swap, giving D, A, C, B. The final positions are shaded. The three choices have 4, 3 and 2 options, 24 combinations in all, one for each of the 24 orders.
The lower panel draws the weights 1, 3, 0 and 6 as intervals of a line from 0 to 10, the total weight: index 0 covers 0 to 1, index 1 covers 1 to 4, index 2 has weight 0 and an empty interval at 4, and index 3 covers 4 to 10. A random u of 0.63 gives the point 6.3, which falls in the interval of index 3. Each index is hit with probability equal to its share of the line.
The brute force, and where it breaks
Problem A can list every order and pick one: 24 orders for four finalists, but 20! has 19 digits and 10⁶! has 5,565,709. The Fisher–Yates shuffle needs only n − 1 random choices instead. Problem B can store every ticket and sample at the end, with memory for the whole stream, while reservoir sampling holds only the k tickets it may return. Problem C can expand every stall into one copy per token, about 5 × 10¹² copies in the run, or scan the running totals for every pick, about 5 × 10⁹ steps for 10⁵ picks among 10⁵ stalls.
# The obvious approach to each of the three problems, and what it costs at the stated sizes.
import itertools
import math
import random
rng = random.Random(2026)
# Problem A: list every order and pick one of them uniformly.
finalists = ["R101", "R102", "R103", "R104"]
orders = list(itertools.permutations(finalists))
print(f"4 finalists: {len(orders)} orders, one picked: {list(orders[rng.randrange(len(orders))])}")
for n in (10, 20, 1_000_000):
digits = len(str(math.factorial(n))) if n < 1000 else int(math.lgamma(n + 1) / math.log(10)) + 1
print(f"{n:,} entries: {n}! has {digits:,} digits")
# Problem B: keep the whole stream, then sample k = 5 at the end.
kept = [f"T{i:07d}" for i in range(200_000)] # 200,000 scanned tickets held in memory
winners = rng.sample(kept, 5)
print(f"stream of {len(kept):,} tickets stored to pick {len(winners)} winners")
# Problem C: a linear scan of the running totals for every pick.
weights = [rng.randint(0, 10**9) for _ in range(10_000)]
total = sum(weights)
steps = 0
for _ in range(1_000):
target = rng.random() * total
running = 0
for i, w in enumerate(weights):
steps += 1
running += w
if running > target:
break
print(f"1,000 picks among 10,000 stalls: {steps:,} steps, about {steps // 1000:,} per pick")
print(f"expanding every stall into one copy per unit of weight: {total:,} copies") Output
4 finalists: 24 orders, one picked: ['R101', 'R103', 'R104', 'R102'] 10 entries: 10! has 7 digits 20 entries: 20! has 19 digits 1,000,000 entries: 1000000! has 5,565,709 digits stream of 200,000 tickets stored to pick 5 winners 1,000 picks among 10,000 stalls: 4,953,653 steps, about 4,953 per pick expanding every stall into one copy per unit of weight: 4,944,655,828,737 copies
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What to notice
- An order is uniform when each position, filled from the end, gets a uniform choice among the items not yet placed.
- A stream item only needs the right chance of entering when it arrives, k / i for the i-th item, without knowing what comes later.
- The running totals cut [0, W) into one interval per stall, so a uniform point lands in stall i’s interval with probability wᵢ / W, and finding the interval is a binary search.
Fisher–Yates, and why each order has probability 1/n! Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Two off-by-one versions, and what they really produce
- Uniformity measured: a million shuffles and a random comparator
- Reservoir sampling, and why each ticket stays with probability k/N
- Weighted picks with running totals and binary search
- Rejection sampling: a fair 1 to 10 from a fair 1 to 7
- Seeds, reproducible tests and the generators of five languages
- The code in five languages
- Edge cases and common bugs
- Variants: Floyd’s sample, a partial shuffle, a weighted reservoir and a point in a disc
- Exercise: pick stalls by weight
- Pick stalls by weight with prefix sums
- A practice set with sample answers
- Check yourself
- Interview questions
- Prove that Fisher-Yates produces every order of n items with probability 1/n!. Then show why the version that swaps each position with a uniformly random position anywhere in the list cannot be uniform, using n = 3.
- Why is sorting a list with a comparator that returns a random answer not a fair way to shuffle it?
- Keep k uniformly random items from a stream of unknown length in O(k) memory. Prove that every item is kept with probability k/N at the end, and say what changes when the stream is split across several machines.
- A weighted picker must answer picks and also change the weight of one item thousands of times between picks. Running totals need O(n) per change. Which structure makes both a pick and a change O(log n), and how exactly does a pick work on it?
- Key takeaways
7 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 25 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- Algorithm 235: Random permutation (R. Durstenfeld, Communications of the ACM 7(7), 1964) (Association for Computing Machinery)
- An algorithm to generate a random cyclic permutation (S. Sattolo, Information Processing Letters 22(6), 1986) (Elsevier)
- Random sampling with a reservoir (J. S. Vitter, ACM Transactions on Mathematical Software 11(1), 1985) (Association for Computing Machinery)
- Reservoir-sampling algorithms of time complexity O(n(1 + log(N/n))) (K.-H. Li, ACM TOMS 20(4), 1994) (Association for Computing Machinery)
- A linear algorithm for generating random numbers with a given distribution (M. D. Vose, IEEE TSE 17(9), 1991) (IEEE)
- Programming pearls: a sample of brilliance (J. Bentley with B. Floyd, Communications of the ACM 30(9), 1987) (Association for Computing Machinery)
- Weighted random sampling with a reservoir (P. S. Efraimidis and P. G. Spirakis, Information Processing Letters 97(5), 2006) (Elsevier)
- Xorshift RNGs (G. Marsaglia, Journal of Statistical Software 8(14), 2003) (Journal of Statistical Software)
- The Python Standard Library: random (Python Software Foundation)
- The Python Standard Library: secrets (Python Software Foundation)
- Java SE 25 API: java.util.Random (Oracle)
- Java SE 25 API: Collections.shuffle (Oracle)
- Working Draft, Programming Languages: C++ (N4950), [rand.dist.general] and [rand.predef] (ISO/IEC JTC1/SC22/WG21)
- Go standard library: package math/rand/v2 (The Go Authors)
- ECMAScript 2026 Language Specification: Math.random (Ecma International)
- ECMAScript 2026 Language Specification: Array.prototype.sort (Ecma International)
- NIST/SEMATECH e-Handbook of Statistical Methods: critical values of the chi-square distribution (National Institute of Standards and Technology)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress