Randomized algorithms are sometimes described as algorithms that “get lucky.” That description hides the part that makes them useful: the random choices are governed by a probability distribution, so their cost or error can be analyzed just as deliberately as a deterministic loop. Randomness is not a substitute for a proof. It is an input over which the proof quantifies.

The key distinction is what randomness is allowed to affect. A Las Vegas algorithm always returns a correct result, but its running time varies. A Monte Carlo algorithm finishes within a known cost, but may return an incorrect result with a bounded probability. Confusing those contracts leads to serious API mistakes: a retry can improve a Monte Carlo confidence level, while rerunning a Las Vegas algorithm is not needed for correctness.

This article develops both contracts through randomized selection and randomized verification. It explains expected cost without averaging over imaginary “typical” data, shows how independent repetition amplifies confidence, and turns seeds, generators, and hostile inputs into testable engineering decisions.

Make Probability Part of the Specification

For a deterministic algorithm, a fixed input determines the execution. For a randomized algorithm, fix the input xx and let RR represent the random bits consumed during the run. Cost and output become variables such as T(x,R)T(x,R) and A(x,R)A(x,R). A robust claim quantifies over every valid fixed input and takes probability only over RR:

for every x,ER[T(x,R)]f(x).\text{for every }x,\quad \mathbb{E}_R[T(x,R)] \le f(|x|).

That is stronger than saying the algorithm performs well on average over a convenient input distribution. Random pivoting, for example, can neutralize an already sorted or adversarially ordered array because the pivot distribution is independent of that order. The analysis does not pretend that sorted input is rare.

A complete randomized contract names at least five things:

Contract element Example question
Outcome Must the answer always be correct, or is bounded error allowed?
Probability space Which choices are random, and from what distribution?
Bound Is cost expected, high-probability, or worst-case?
Independence Do repeated trials use independent random bits?
Adversary Can input be chosen after observing seeds, hashes, or prior outputs?

Expected cost is not the same as a deadline. A small expectation can coexist with a long tail. Markov’s inequality gives only the general bound

Pr[TcE[T]]1c,\Pr[T \ge c\,\mathbb{E}[T]] \le \frac{1}{c},

which is often too weak for an operational latency target. A stronger tail claim needs more structure in the algorithm or an explicit fallback. Likewise, a one-in-a-billion error probability is not “correct” in a domain where any false approval is unacceptable. Probability must be connected to the consequence and number of decisions made over the system’s lifetime.

Separate Las Vegas and Monte Carlo Guarantees

A Las Vegas algorithm may vary its search path, but it validates enough information to return only a correct result. Randomized Quickselect is a representative example: it chooses random pivots to find an order statistic. A poor sequence of pivots can make it slow, but partition invariants ensure that the returned element is the requested one.

A Monte Carlo algorithm spends a bounded amount of work and accepts some chance of error. Randomized verification can check a claimed matrix product much faster than recomputing it, but a false product can occasionally pass. The API should return a confidence-qualified decision such as “no mismatch found in 20 independent rounds,” not imply a deterministic proof.

There are two common Monte Carlo error shapes:

  • One-sided error: one class of answer is certain. A mismatch found by a verifier proves the product is wrong; “passes” may be a false acceptance.
  • Two-sided error: either answer may be wrong, with each probability bounded by the analysis.

Repetition behaves differently for the two families. Repeating a Las Vegas run and voting is pointless because every completed answer is already correct. Repeating an independent Monte Carlo trial can reduce error exponentially. If one trial fails with probability at most pp, accepting only when all rr trials accept bounds false acceptance by prp^r.

The word independent is doing real work. Reusing the same pseudo-random vector, deriving every trial from a weak repeated seed, or allowing a client to choose the seed may make the trials perfectly correlated. Ten copies of one choice are one trial wearing ten labels.

Work Through Randomized Quickselect

Suppose the input is [9, 1, 7, 3, 8, 2, 6, 5, 4], and the goal is the element at zero-based sorted index 4: the fifth-smallest value. Choose pivot 8. Partitioning yields seven lower values, one equal value, and one greater value. Because index 4 lies in the lower partition, discard 8 and 9.

On [1, 7, 3, 2, 6, 5, 4], suppose the next pivot is 3. The lower partition [1, 2] has length two, and the equal partition has length one. The desired rank is beyond both, so continue in [7, 6, 5, 4] with adjusted index 421=14-2-1=1. If the next pivot is 5, the lower partition [4] has length one, so index 1 lands on the pivot. The answer is 5.

ts
type RandomIndex = (upperExclusive: number) => number;

export function quickselect(
  input: readonly number[],
  targetIndex: number,
  randomIndex: RandomIndex,
): number {
  if (!Number.isInteger(targetIndex) || targetIndex < 0 || targetIndex >= input.length) {
    throw new RangeError('targetIndex is outside the input');
  }

  let values = [...input];
  let target = targetIndex;

  while (true) {
    const pivot = values[randomIndex(values.length)]!;
    const lower: number[] = [];
    const equal: number[] = [];
    const higher: number[] = [];

    for (const value of values) {
      if (value < pivot) lower.push(value);
      else if (value > pivot) higher.push(value);
      else equal.push(value);
    }

    if (target < lower.length) {
      values = lower;
    } else if (target < lower.length + equal.length) {
      return pivot;
    } else {
      target -= lower.length + equal.length;
      values = higher;
    }
  }
}

The copied partitions make the invariant visible: every value in lower precedes every pivot-equal value in sorted order, which precedes every value in higher. An in-place implementation saves allocation but needs careful boundaries and duplicate handling. Randomness affects only which valid partition is explored, never the rank arithmetic that establishes correctness.

Why is expected work linear? A pivot from the middle half of the current ranks leaves at most three quarters of the values in the surviving partition. A uniformly selected pivot is in that middle half with probability at least 1/21/2. The expected number of pivot attempts before such a shrink is therefore at most two. Charging linear partition work at geometrically shrinking sizes gives a series proportional to

n+3n4+(34)2n+=O(n).n + \frac{3n}{4} + \left(\frac{3}{4}\right)^2n + \cdots = O(n).

This is an expectation over pivot choices for every fixed input ordering. Worst-case time remains O(n2)O(n^2) when pivots repeatedly select extremes. If a hard deadline matters, use an introspective design: track progress and switch to a deterministic linear-time selection method or another bounded fallback after an excessive depth. Expected speed and worst-case protection can coexist.

Bound Monte Carlo Error by Amplification

Freivalds’ algorithm illustrates a different bargain. Given square matrices AA, BB, and a claimed product CC, recomputing ABAB takes cubic work with the elementary algorithm. Instead choose a random vector rr whose entries are independently 0 or 1, then compare

A(Br)=?Cr.A(Br) \stackrel{?}{=} Cr.

The matrix-vector products take O(n2)O(n^2) time. If AB=CAB=C, the equality always holds. If ABCAB\ne C, one round accepts incorrectly with probability at most 1/21/2 over a uniformly random binary vector. This is one-sided Monte Carlo error: observing unequal vectors is a proof of a mismatch, while equality means only that this round did not expose one.

With fresh independent vectors, rr rounds reduce false acceptance to

Pr[false product passes all rounds]2r.\Pr[\text{false product passes all rounds}] \le 2^{-r}.

Twenty rounds give a mathematical bound of 2202^{-20} for one verification under the model; they do not produce “absolute certainty.” If a service performs qq verifications, the union bound gives at most q2rq2^{-r} probability that any false product passes, even without assuming failures across requests are independent. Choose rr from the system-level error budget rather than from a convention.

Arithmetic semantics are part of the proof. Fixed-width integer overflow, floating-point rounding, and modular reduction can change what equality means. An implementation should operate in an exact field or ring appropriate to the theorem, with overflow controlled. Testing a floating-point matrix by exact equality is neither a faithful implementation of the algebra nor a sound numerical tolerance strategy.

Amplification also costs time linearly in the number of rounds. Stop early on the first mismatch, but never stop early because several passes “look convincing” unless that adaptive rule is included in the bound. Record the round count and generator version with the result so confidence remains interpretable.

Analyze Expectation Without Hiding Tail Risk

Linearity of expectation is the main workhorse. For variables X1,,XmX_1,\ldots,X_m, it states

E[iXi]=iE[Xi],\mathbb{E}\left[\sum_i X_i\right]=\sum_i\mathbb{E}[X_i],

even when the variables are dependent. Indicator variables make this especially useful: define Xi=1X_i=1 when an event occurs and 0 otherwise, then E[Xi]=Pr[Xi=1]\mathbb{E}[X_i]=\Pr[X_i=1]. Expected comparisons, collisions, retries, and sampled items often reduce to summing such probabilities.

But an expected bound alone answers only one question. An algorithm used in a request path may also need a high-probability bound, a maximum amount of allocated memory, or cancellation. Quickselect’s expected linear cost does not stop one unlucky request from taking quadratic time. A randomized backoff may have good expected contention but still need a retry cap. State each layer separately:

Guarantee What it permits the caller to conclude
Always correct Completed output satisfies the specification
Expected O(n)O(n) Mean cost over fresh random choices grows linearly
With probability 1δ1-\delta The stated event fails in at most a δ\delta fraction under the model
Worst-case O(f(n))O(f(n)) No valid execution exceeds the asymptotic bound

Expectation can also be invalidated by conditioning. Logging only successful fast runs, dropping timed-out trials, or retrying until a preferred answer appears changes the observed distribution. Include all attempts and failures when evaluating cost. For Monte Carlo methods, retries must combine evidence according to the proof; selecting whichever output is convenient destroys the bound.

Treat Randomness as a Dependency and Attack Surface

A pseudo-random generator is deterministic state expansion. That is useful: a compact seed can reproduce a failing execution. It does not mean every generator is statistically adequate or unpredictable. Choose the source according to the consequence.

Simulation, randomized data structures under trusted input, and tests often need a fast generator with documented distribution and reproducible seeding. Security-sensitive sampling, secret generation, or algorithms facing adaptive attackers need a cryptographically secure source. A timestamp seed has little entropy and may repeat across processes. Applying % n to a generator range not divisible by nn introduces modulo bias unless rejection sampling removes the uneven tail.

The adversary model determines whether randomization still helps. If an attacker sees a fixed hash seed and can submit keys adaptively, they may engineer collision-heavy behavior. If a public API accepts the Quickselect seed, a caller may force poor pivots. Keep defensive seeds private, rotate them at safe boundaries, and avoid exposing enough state to predict future choices.

Reproducibility and unpredictability can conflict. Production incident records can log a protected seed identifier rather than raw secret state, then reproduce the run in an appropriately controlled environment. For non-security algorithms, logging the exact seed is usually simpler. In either case, version the generator: the same numeric seed fed to a different algorithm does not promise the same sequence.

Randomization also cannot repair an invalid invariant. A random pivot does not make incorrect partition boundaries correct. A random retry does not make a non-idempotent operation safe. First prove deterministic behavior conditioned on arbitrary valid random choices; then analyze how their distribution affects cost or error.

Test Correctness, Distribution, and Reproducibility

Tests for randomized code should make failure reproducible without reducing coverage to one fortunate path. Inject the random source, as in quickselect, and run a stable matrix of seeds. On failure, report the seed, input, generator version, and number of random draws.

For Quickselect, compare every target index against a sorted oracle on empty-boundary, singleton, duplicate-heavy, already sorted, reverse-sorted, and generated arrays. Force the random dependency to select the first, last, and middle index repeatedly so partition edges execute deterministically. Check metamorphic properties: permuting the input does not change the selected value, and adding a constant to every value adds that constant to the result.

Separate performance-shape tests from correctness. Count comparisons or partitioned elements for controlled pivot sequences rather than asserting millisecond thresholds. Verify that the fallback activates after its configured progress limit. A test for expected behavior can aggregate many seeds, but its tolerance must be derived generously enough to avoid failing normal variance; deterministic invariants remain the primary gate.

For Monte Carlo verification, enumerate small matrices where exact multiplication is cheap. Every correct product must pass every seed because the error is one-sided. Known incorrect products should be exposed across the seed suite, while a deterministic test can inject a vector that specifically reveals the mismatch. Statistical experiments can check that empirical false acceptance does not contradict the theoretical cap, but passing such an experiment does not prove the implementation. Algebraic unit tests and exact arithmetic checks carry that burden.

Test independence plumbing as well. Assert that amplification consumes fresh draws, serialized generator state resumes intentionally, and concurrent calls do not accidentally share mutable generator state. Include malformed bounds such as randomIndex(n) === n; fail at the dependency boundary rather than corrupting array access.

Choose Randomization for a Stated Reason

Randomization is attractive when it removes dependence on hostile input order, gives a simpler or faster algorithm with quantified expected cost, or trades a controlled error probability for a major reduction in work. It is less attractive when exact deterministic alternatives already meet the budget, when results must be independently reproducible without retained seed state, or when even a tiny error has no acceptable containment.

Review the decision with concrete questions:

  1. Is this Las Vegas or Monte Carlo, and can the API communicate that honestly?
  2. Is the bound per operation or across the lifetime volume of operations?
  3. Which assumptions require uniformity, independence, exact arithmetic, or a hidden seed?
  4. What happens in the unlucky tail or after the retry budget is exhausted?
  5. Can a failing execution be replayed from recorded state?
  6. Does a deterministic oracle exist for small test cases?

The discipline is compact. Prove correctness for arbitrary valid random choices when correctness must be absolute. Analyze expected work or error over a named probability space. Amplify only with independent trials. Add a deterministic fallback when a tail bound is operationally insufficient. Inject and version the generator so tests can reproduce the path.

Randomized algorithms do not ask engineers to trust luck. They ask engineers to specify uncertainty precisely enough that it becomes calculable. Once the outcome, distribution, adversary, and fallback are explicit, randomness is simply another resource: one that can buy speed, simplicity, or robustness without giving up rigor.