When to use a randomized algorithm
Use randomness when you need to:
- Shuffle items so that every order is equally likely: a deck of cards, a playlist, the order of test cases, a random sample of users.
- Pick a fair random sample from a stream whose length you do not know, using fixed memory: reservoir sampling.
- Avoid a bad worst case that an adversary or an unlucky input could trigger, as in quicksort with a random pivot.
- Break ties or spread load evenly, as in random choice between two servers.
Use something else when:
- You need cryptographic randomness (keys, tokens, passwords): use your language's secure random source, not the ordinary generator.
- You need repeatable results (tests, simulations): use a seeded generator and record the seed.
- You need a sort order that is stable and known: do not shuffle, sort.
- A deterministic algorithm is simple and fast enough.
Do not write your own shuffle or generator in production. Use Collections.shuffle, std::shuffle, random.shuffle, rand.Shuffle or the like (see the real-world section). Writing Fisher-Yates by hand is for learning and interviews, and a classic place for a subtle bug.
How randomized algorithms work
There are two families. A Las Vegas algorithm always gives a correct answer and only its running time is random (randomized quicksort always sorts correctly). A Monte Carlo algorithm runs in a fixed time but its answer may be wrong with a small, known probability (estimating a value by random samples). Everything on this page except the Monte Carlo example in the real-world section is Las Vegas style, where "random" affects the path taken, not the correctness.
A randomized algorithm uses random choices as part of its logic. The skill is not "adding randomness" but making sure the random choices produce the distribution you promised. The classic mistake is a shuffle that looks random but is biased.
Fairness rule: a shuffle of
nitems must be able to produce each of then!orders in exactly the same number of ways.
- Fisher-Yates goes from the last position down to 1. At position
iit picks a random positionjfrom0toi(includingi) and swaps. Positioniis then final. The number of choices isn × (n-1) × ... × 2 = n!, one per order, so every order is equally likely. - A naive shuffle swaps each position with a random position from the whole array. That makes
n × n × ... × n = nⁿequally likely choices. For three items that is 27, and 27 cannot be split evenly among 6 orders, so some orders come out more often than others. It is biased. - Reservoir sampling keeps one (or
k) items from a stream. Item numberireplaces the kept item with probability1 / i. An early item starts with a high chance of being kept but has many later chances of being replaced. A late item has a low chance of being kept at first but few chances of being replaced. The two effects cancel exactly, so every item ends up kept with the same probability1 / n.
Try it
Start with the biased shuffle and Count every possible outcome. Then compare the others.
Randomized Algorithms
Step 1 of 2 · Press Space to play, arrow keys to stepArray
Initial state. Shuffle A B C so that every one of the 6 orders is equally likely.
- step:
- 0
- random numbers used:
- 0
Things to try:
- Choose Naive shuffle and Count every possible outcome. There are 27 equally likely choices. Three orders appear 5 times and three appear only 4 times: it is biased. Now choose Fisher-Yates and see every one of the 6 orders appear exactly once.
- Choose Fisher-Yates and One run, step by step. Watch position 2, then position 1, get a random swap partner. Each position is final once it has been processed.
- Choose Reservoir sampling and Count every possible outcome. Each of the 4 stream items is kept in exactly 6 of the 24 outcomes, so each has probability 1 in 4.
- Watch the most likely and least likely counters under the naive shuffle (5 and 4). The gap is the bias. Under Fisher-Yates both counters are 1.
- Choose Reservoir sampling and One run, step by step a few times: the kept item changes often early on and rarely late, yet the final answer is equally likely to be any item.
- One run proves nothing about fairness. Only counting every outcome does.
A dry run on three items
Shuffle A B C. Fisher-Yates handles position 2, then position 1, so there are 3 × 2 = 6 choices:
| Choice at position 2 | Array after it | Choice at position 1 | Final order |
|---|---|---|---|
| j = 0 | C B A | j = 0 | B C A |
| j = 0 | C B A | j = 1 | C B A |
| j = 1 | A C B | j = 0 | C A B |
| j = 1 | A C B | j = 1 | A C B |
| j = 2 | A B C | j = 0 | B A C |
| j = 2 | A B C | j = 1 | A B C |
All six orders appear exactly once, so each has probability 1/6.
The naive shuffle swaps each of the 3 positions with any of the 3 positions, giving 3 × 3 × 3 = 27 equally likely choices. Counting what they produce gives BCA, BAC and ACB five times each, and CAB, CBA and ABC four times each (3 × 5 + 3 × 4 = 27). Since 27 is not a multiple of 6, equal counts are impossible. BCA has probability 5/27 (about 18.5%) and ABC has 4/27 (about 14.8%).
Reservoir sampling of one item from A B C D. Take B: it enters at step 2 with probability 1/2, survives step 3 with probability 2/3 and survives step 4 with probability 3/4. The product is 1/2 × 2/3 × 3/4 = 1/4. The same product for any other item is also 1/4.
Randomized algorithm operations: shuffle, sample and choose a pivot
Fisher-Yates shuffle.
- Start at the last position
i = n - 1. - Pick
jat random from0toi(both included). - Swap
a[i]anda[j]. - Move
idown by one, and repeat untiliis 1.
Reservoir sampling (one item).
- Keep the first item.
- For item number
i(counting from 1), pick a random number from 1 toi. If it is 1, replace the kept item with this one. - After the last item, the kept item is a uniform random choice.
Reservoir sampling (k items). Fill the reservoir with the first k items. For item number i after that, pick a random j from 0 to i - 1. If j is less than k, put the item at position j of the reservoir.
Random pivot in quicksort or quickselect. Pick the pivot position at random. No fixed input can force the worst case, because the pivot is not known in advance. The expected time is O(n log n) for sorting and O(n) for quickselect.
Why the bounds hold.
- Reservoir sampling with
kitems. The item at positioni(counting from 1, withiabovek) enters with probabilityk / i. A kept item is replaced at stepjwith probability1 / j(the new item is picked withk / j, and it lands on this particular slot with1 / k), so it survives with(j - 1) / j. The product over stepsi + 1tontelescopes toi / n, andk / i × i / n = k / n. The firstkitems are kept for certain and survive each later step the same way, which also givesk / n. - Random pivot. A pivot that lands in the middle half of the sorted order has probability 1/2. When it does, each side holds at most three quarters of the items, so the problem shrinks by a constant factor. On average two tries are needed per shrink, so the depth is
O(log n)levels ofO(n)work.
A note on random numbers. To get a number from 0 to i you must draw uniformly from exactly i + 1 values. Taking an ordinary random number and using % (i + 1) slightly favours small values unless the range divides evenly. Library functions such as rand.Shuffle or random.randrange handle this for you.
A shuffle must pick from 0 to i, not from the whole array. This is the interview fact: that single detail separates a correct shuffle from a biased one.
Randomized algorithm time complexity
| Algorithm | Time | Space | Guarantee |
|---|---|---|---|
| Fisher-Yates shuffle | O(n) | O(1) extra | Every order equally likely |
| Naive shuffle (swap with any position) | O(n) | O(1) extra | Biased: not every order is equally likely |
| Reservoir sampling, one item | O(n) | O(1) | Each of n items kept with probability 1/n |
Reservoir sampling, k items | O(n) | O(k) | Each item kept with probability k/n |
| Randomized quicksort | O(n log n) expected, O(n²) worst | O(log n) expected stack | Worst case is very unlikely, whatever the input |
| Randomized quickselect | O(n) expected, O(n²) worst | O(1) extra | Same idea as quicksort |
n is the number of items and k the sample size. "Expected" means averaged over the random choices, not over the inputs, so a hostile input cannot hurt it.
Randomized algorithms vs other approaches
| Job | Fisher-Yates | Sort by random key | Reservoir sampling | Deterministic approach |
|---|---|---|---|---|
| Shuffle an array | O(n), fair | O(n log n), fair if keys are unique | Not applicable | Not possible |
Sample k from a known-size list | O(k) with partial shuffle | O(n log n) | O(n) | Pick first k (not random) |
Sample k from a stream of unknown length | Not possible | Not possible | O(n), O(k) memory | Store everything: O(n) memory |
| Avoid worst-case quicksort | Not applicable | Not applicable | Not applicable | Median of medians: O(n), complex |
| Repeatable results | With a seeded generator | With a seeded generator | With a seeded generator | Always |
| Easy to get wrong | Off-by-one in the range | Sorting by a random comparator is biased | Probability 1/i | Simple |
n is the number of items. Fisher-Yates wins for shuffling, and reservoir sampling wins when the data does not fit in memory or its length is unknown. Never sort with a random comparator such as sort(() => Math.random() - 0.5): it is biased and its behaviour depends on the sort algorithm.
How to recognise a randomized-algorithm problem
Look for these phrases in a problem statement:
- "Every permutation / subset / element must be equally likely" means a fairness argument, usually Fisher-Yates or a sampling method.
- "Stream", "unknown length", "cannot store everything" or "one pass" means reservoir sampling.
- "Pick with probability proportional to a weight" means prefix sums plus binary search (see the interview section).
- "Expected time", "no input should be bad" or "adversarial input" means a random pivot or random hash.
- "Approximately", "estimate" or "with high probability" means a Monte Carlo method.
Then ask three questions. What distribution do I promise? Can I count the equally likely choices? Does the answer have to be exact (Las Vegas) or may it be wrong rarely (Monte Carlo)?
Edge cases to check
- 0 or 1 items. Fisher-Yates loops from
n - 1down to 1, so it does nothing and returns the input, which is the only possible order. kat leastnin reservoir sampling. The reservoir never fills past the stream, so you get every item. Decide whether that is allowed.- Empty stream. One-item reservoir sampling has nothing to return, so return "none" explicitly.
- Duplicate values. The shuffle treats positions, not values, so equal values are fine, but the number of distinct outputs is smaller than
n!. - Sorted or all-equal input to randomized quicksort. Sorted input is no longer special. A plain two-way partition on all-equal input still degrades, so use three-way partitioning.
- Repeatability. A fixed seed gives the same result every run, which is what tests need, and what secrets must never have.
Common mistakes
- Picking
jfrom the whole array in a shuffle (n^nchoices, biased), or from0toi - 1(excludesi, which is Sattolo's algorithm: only single-cycle orders, so no item stays in place). - Sorting with a random comparator such as
sort(() => Math.random() - 0.5): inconsistent comparisons, biased result that depends on the sort algorithm. - Using probability
1 / nor a fixed1 / 2in reservoir sampling instead of1 / ifor thei-th item. - Counting from 0 and using
1 / ifor the first item. The first item must be kept with probability 1. - Using an ordinary generator for passwords, keys or tokens.
- Re-seeding the generator inside a loop (for example with the clock), which repeats values.
- Trusting a single run to show fairness. Fairness is a statement about all outcomes, which is why the simulation counts them.
How to explain randomized algorithms in an interview
- Say what distribution you promise ("every permutation equally likely") and show why with a counting argument: Fisher-Yates has
n!equally likely choices, so each order has probability1 / n!. - For reservoir sampling, give the probability proof: item
iis kept at stepiwith probability1/i, then survives each later stepjwith probability1 - 1/j; the product telescopes to1/n. - Say "expected" versus "worst case" for randomized quicksort and why a hostile input cannot target it.
- Add a practical note: use the library for shuffles, a secure generator for secrets, and a fixed seed in tests.