-
Notifications
You must be signed in to change notification settings - Fork 7
/
pivot.go
81 lines (73 loc) · 2.04 KB
/
pivot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package pdqsort
// choosePivot chooses a pivot in `v` and returns the index and `true` if the slice is likely already sorted.
//
// Elements in `v` might be reordered in the process.
//
// [0,8): chooses a static pivot.
// [8,ShortestNinther): uses the simple median-of-three method.
// [ShortestNinther,∞): uses the Tukey’s ninther method.
func choosePivot[T ordered](v []T) (pivotidx int, likelySorted bool) {
const (
// shortestNinther is the minimum length to choose the Tukey’s ninther method.
// Shorter slices use the simple median-of-three method.
shortestNinther = 50
// maxSwaps is the maximum number of swaps that can be performed in this function.
maxSwaps = 4 * 3
)
l := len(v)
var (
// Counts the total number of swaps we are about to perform while sorting indices.
swaps int
// Three indices near which we are going to choose a pivot.
a = l / 4 * 1
b = l / 4 * 2
c = l / 4 * 3
)
if l >= 8 {
if l >= shortestNinther {
// Find medians in the neighborhoods of `a`, `b`, and `c`.
a = sortAdjacent(v, a, &swaps)
b = sortAdjacent(v, b, &swaps)
c = sortAdjacent(v, c, &swaps)
}
// Find the median among `a`, `b`, and `c`.
b = sort3(v, a, b, c, &swaps)
}
if swaps < maxSwaps {
return b, (swaps == 0)
} else {
// The maximum number of swaps was performed. Chances are the slice is descending or mostly
// descending, so reversing will probably help sort it faster.
reverseRange(v)
return (l - 1 - b), true
}
}
// sort3 swaps `a` `b` `c` so that `v[a] <= v[b] <= v[c]`, then returns `b`.
func sort3[T ordered](v []T, a, b, c int, swaps *int) int {
if v[b] < v[a] {
*swaps++
a, b = b, a
}
if v[c] < v[b] {
*swaps++
b, c = c, b
}
if v[b] < v[a] {
*swaps++
a, b = b, a
}
return b
}
// sortAdjacent finds the median of `v[a - 1], v[a], v[a + 1]` and stores the index into `a`.
func sortAdjacent[T ordered](v []T, a int, swaps *int) int {
return sort3(v, a-1, a, a+1, swaps)
}
func reverseRange[T ordered](v []T) {
i := 0
j := len(v) - 1
for i < j {
v[i], v[j] = v[j], v[i]
i++
j--
}
}