Sort a fixed size sequence with a bitonic compare and exchange network whose schedule is independent of input values.
A bitonic sorting network is a fixed sequence of compare and exchange operations that sorts a fixed number of elements. It is based on the idea of repeatedly building bitonic sequences and merging them.
A bitonic sequence increases and then decreases, or can be rotated into that shape. The network first creates bitonic sequences of small size, then merges them into larger sorted sequences until the full input is sorted.
Problem
Given input values, where is usually a power of two, sort them in nondecreasing order using a fixed comparator network.
Unlike quicksort or merge sort, the comparison schedule does not depend on the input values.
Algorithm
The iterative network form uses nested stages.
bitonic_sorting_network(A):
n = length(A)
for size = 2; size <= n; size *= 2:
for stride = size / 2; stride > 0; stride /= 2:
for i from 0 to n - 1:
j = i xor stride
if j > i:
ascending = (i & size) == 0
if ascending and A[i] > A[j]:
swap A[i], A[j]
if not ascending and A[i] < A[j]:
swap A[i], A[j]The partner index is:
The value of stride decides which wires are compared in the current stage.
Comparator
Each comparator sorts a pair of positions.
compare_exchange(A, i, j, ascending):
if ascending:
if A[i] > A[j]:
swap A[i], A[j]
else:
if A[i] < A[j]:
swap A[i], A[j]In a hardware or SIMD implementation, all independent comparators in the same stage can run at the same time.
Example
For , the network performs these stages:
compare (0, 1) ascending
compare (2, 3) descending
compare (0, 2) ascending
compare (1, 3) ascending
compare (0, 1) ascending
compare (2, 3) ascendingStarting with:
The final output is:
Complexity
| measure | value |
|---|---|
| comparators | |
| network depth | |
| extra space | |
| input dependence | none |
The same comparator pattern runs for sorted, reversed, random, and duplicate heavy inputs.
Correctness
The network builds sorted sequences by repeatedly applying bitonic merge. A bitonic merge takes a bitonic sequence and turns it into a sorted sequence by first comparing elements separated by half the sequence length, then recursively merging both halves.
The construction ensures that each larger stage receives valid bitonic input. By induction over sequence size, every stage produces correctly sorted subsequences. At the final stage, the full sequence is sorted.
Practical Considerations
- It maps well to hardware, SIMD, and GPUs.
- It has predictable control flow.
- It performs more comparisons than many adaptive or comparison based sorts.
- It works best for power of two lengths.
- Non power of two inputs can be padded with sentinel values.
- It is often used as a local sorting primitive rather than a full CPU array sort.
When to Use
Use a bitonic sorting network when:
- input size is fixed
- predictable execution matters
- parallel compare and exchange is cheap
- SIMD, GPU, FPGA, or hardware sorting is targeted
- branches and input dependent control flow should be avoided
For general CPU sorting of large arrays, introsort, merge sort, radix sort, or sample sort usually performs less work.
Implementation
func BitonicSortingNetwork(a []int) {
n := len(a)
for size := 2; size <= n; size <<= 1 {
for stride := size >> 1; stride > 0; stride >>= 1 {
for i := 0; i < n; i++ {
j := i ^ stride
if j > i {
ascending := (i & size) == 0
if ascending && a[i] > a[j] {
a[i], a[j] = a[j], a[i]
}
if !ascending && a[i] < a[j] {
a[i], a[j] = a[j], a[i]
}
}
}
}
}
}def bitonic_sorting_network(a):
n = len(a)
size = 2
while size <= n:
stride = size // 2
while stride > 0:
for i in range(n):
j = i ^ stride
if j > i:
ascending = (i & size) == 0
if ascending and a[i] > a[j]:
a[i], a[j] = a[j], a[i]
if not ascending and a[i] < a[j]:
a[i], a[j] = a[j], a[i]
stride //= 2
size *= 2