Skip to content

Randomized Quicksort

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

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 AA of length nn, reorder it such that:

A[0]A[1]A[n1] 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 lolo and hihi, 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] A = [4, 2, 7, 1, 5]

Suppose a random pivot index selects value 22.

Swap with last element:

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

Partition around 22:

[1,2,7,4,5] [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

casetime
expectedO(nlogn)O(n \log n)
worstO(n2)O(n^2)

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

Expected recurrence:

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

where kk is random.

Properties

propertyvalue
stableno
in-placeyes
randomizedyes
expected performancestrong

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
}