Shell Sort with Hibbard Gaps
Shell sort using Hibbard gap sequence 1, 3, 7, 15, ..., improving over simple halving.
Shell Sort with Hibbard Gaps
Shell sort with Hibbard gaps replaces the simple halving sequence with a more structured progression:
$$ g_k = 2^k - 1 $$
This sequence produces gaps such as:
$$ 1, 3, 7, 15, 31, \dots $$
In practice, you use the largest value less than $n$, then proceed in decreasing order down to 1.
This improves performance over the basic Shell gap sequence by reducing long-distance disorder more effectively.
Problem
Given a sequence $A$ of length $n$, reorder it such that
$$ A[0] \le A[1] \le \cdots \le A[n-1] $$
Gap Sequence
Construct all values of the form:
$$ 2^k - 1 < n $$
Sort them in descending order and use them as gaps.
Example for $n = 20$:
$$ 15, 7, 3, 1 $$
Algorithm
For each gap, perform insertion sort over elements spaced by that gap.
shell_sort_hibbard(A):
n = length(A)
gaps = []
k = 1
while (2^k - 1) < n:
gaps.append(2^k - 1)
k = k + 1
for gap in reverse(gaps):
for i from gap to n - 1:
key = A[i]
j = i
while j >= gap and A[j - gap] > key:
A[j] = A[j - gap]
j = j - gap
A[j] = key
Example
Let
$$ A = [9, 8, 3, 7, 5, 6, 4, 1] $$
For $n = 8$, Hibbard gaps are:
$$ 7, 3, 1 $$
Gap 7:
→ compare elements far apart
Gap 3:
→ reduce disorder further
Gap 1:
→ final insertion sort
Result:
$$ [1, 3, 4, 5, 6, 7, 8, 9] $$
Correctness
Each gap sorts subsequences formed by stepping through the array with stride $g$. As gaps decrease, elements move closer to their correct positions. The final gap of 1 ensures a fully sorted sequence.
Complexity
| case | time |
|---|---|
| worst | $O(n^{3/2})$ |
| average | about $O(n^{3/2})$ |
| best | $O(n \log n)$ |
Space complexity:
$$ O(1) $$
Properties
- in-place
- not stable
- better theoretical bounds than simple gaps
When to Use
Hibbard gaps provide a good balance between simplicity and performance. They are suitable when you want a predictable improvement over naive Shell sort without complex tuning.
Implementation
def shell_sort_hibbard(a):
n = len(a)
gaps = []
k = 1
while (2**k - 1) < n:
gaps.append(2**k - 1)
k += 1
for gap in reversed(gaps):
for i in range(gap, n):
key = a[i]
j = i
while j >= gap and a[j - gap] > key:
a[j] = a[j - gap]
j -= gap
a[j] = key
return a
func ShellSortHibbard[T constraints.Ordered](a []T) {
n := len(a)
var gaps []int
for k := 1; (1<<k)-1 < n; k++ {
gaps = append(gaps, (1<<k)-1)
}
for g := len(gaps) - 1; g >= 0; g-- {
gap := gaps[g]
for i := gap; i < n; i++ {
key := a[i]
j := i
for j >= gap && a[j-gap] > key {
a[j] = a[j-gap]
j -= gap
}
a[j] = key
}
}
}