Randomized Quicksort

Quicksort variant that selects pivots randomly to avoid worst case patterns.

Randomized Quicksort

Randomized quicksort modifies quicksort by choosing the pivot at random. This removes dependence on input order and prevents consistently bad partitions.

The algorithm retains the same structure as quicksort but replaces deterministic pivot selection with a random choice.

Problem

Given an array $A$ of length $n$, reorder it such that:

$$ A[0] \le A[1] \le \dots \le A[n-1] $$

Idea

Instead of always choosing a fixed pivot position, pick a random index between $lo$ and $hi$, then swap it with the last element and use it as the pivot.

This makes every permutation equally likely, so worst case behavior becomes extremely unlikely.

Algorithm

randomized_quicksort(A, lo, hi):
    if lo >= hi:
        return

    p = randomized_partition(A, lo, hi)

    randomized_quicksort(A, lo, p - 1)
    randomized_quicksort(A, p + 1, hi)
randomized_partition(A, lo, hi):
    r = random integer in [lo, hi]
    swap A[r] and A[hi]

    return partition(A, lo, hi)

The partition step is the same as standard quicksort.

Example

Let:

$$ A = [4, 2, 7, 1, 5] $$

Suppose a random pivot index selects value $2$.

Swap with last element:

$$ [4, 5, 7, 1, 2] $$

Partition around $2$:

$$ [1, 2, 7, 4, 5] $$

Then recursively sort left and right parts.

Correctness

Randomization changes only pivot selection. The partition step still ensures that the pivot is placed between smaller and larger elements. The recursive structure remains valid, so correctness follows from standard quicksort.

Complexity

case time
expected $O(n \log n)$
worst $O(n^2)$

The worst case remains possible but occurs with very low probability.

Expected recurrence:

$$ T(n) = T(k) + T(n-k-1) + O(n) $$

where $k$ is random.

Properties

property value
stable no
in-place yes
randomized yes
expected performance strong

Notes

Randomization protects quicksort from adversarial inputs. It is widely used in practice because it is simple and effective.

In many implementations, a pseudo random generator is used, which is sufficient for performance purposes.

Implementation

import random

def randomized_quicksort(a):
    def sort(lo, hi):
        if lo >= hi:
            return

        p = partition(lo, hi)
        sort(lo, p - 1)
        sort(p + 1, hi)

    def partition(lo, hi):
        r = random.randint(lo, hi)
        a[r], a[hi] = a[hi], a[r]

        pivot = a[hi]
        i = lo

        for j in range(lo, hi):
            if a[j] <= pivot:
                a[i], a[j] = a[j], a[i]
                i += 1

        a[i], a[hi] = a[hi], a[i]
        return i

    sort(0, len(a) - 1)
    return a
import "math/rand"

func RandomizedQuickSort(a []int) {
	var sort func(int, int)

	sort = func(lo, hi int) {
		if lo >= hi {
			return
		}

		p := randomizedPartition(a, lo, hi)
		sort(lo, p-1)
		sort(p+1, hi)
	}

	sort(0, len(a)-1)
}

func randomizedPartition(a []int, lo, hi int) int {
	r := lo + rand.Intn(hi-lo+1)
	a[r], a[hi] = a[hi], a[r]

	pivot := a[hi]
	i := lo

	for j := lo; j < hi; j++ {
		if a[j] <= pivot {
			a[i], a[j] = a[j], a[i]
			i++
		}
	}

	a[i], a[hi] = a[hi], a[i]
	return i
}