-
Notifications
You must be signed in to change notification settings - Fork 2
/
sorterX.go
125 lines (114 loc) · 2.99 KB
/
sorterX.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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package SimpleRTree
// This is copy paste from floyd rivest to use concrete types. Perf gain is 2.5x in load times
import (
"math"
)
// Buckets. Sort a slice into buckets of given size. All elements from one bucket are smaller than any element from the next one.
// elements at position i * bucketSize are guaranteed to be the (i * bucketSize) th smallest elements
// s := // some slice
// FloydRivest.Buckets(sort.Interface(s), 5)
// s is now sorted into buckets of size 5
// max(s[0:5]) < min(s[5:10])
// max(s[10: 15]) < min(s[15:20])
// ...
func bucketsX(slice xSorter, bucketSize int, buffer []int) {
left := 0
right := slice.Len() - 1
stack := buffer[:0]
stack = append(stack, left)
stack = append(stack, right)
s := ySorterStack(stack)
var mid int
for len(s) > 0 {
s, right = s.pop()
s, left = s.pop()
if right-left <= bucketSize {
continue
}
// + bucketSize - 1 is to do math ceil
mid = left + ((right-left+bucketSize-1)/bucketSize/2)*bucketSize
selectX(slice, mid, left, right)
s = s.push(left)
s = s.push(mid)
s = s.push(mid)
s = s.push(right)
}
}
// left is the left index for the interval
// right is the right index for the interval
// k is the desired index value, where array[k] is the k+1 smallest element
// when left = 0
func selectX(array xSorter, k, left, right int) {
length := array.Len()
for right > left {
if right-left > 600 {
var n = float64(right - left + 1)
var kf = float64(k)
var m = float64(k - left + 1)
var z = math.Log(n)
var s = 0.5 * math.Exp(2*z/3)
sign := float64(1)
if m-n/2 < 0 {
sign = -1
}
var sd = 0.5 * math.Sqrt(z*s*(n-s)/n) * sign
var newLeft = xSorterMax(left, int(math.Floor(kf-m*s/n+sd)))
var newRight = xSorterMin(right, int(math.Floor(kf+(n-m)*s/n+sd)))
selectX(array, k, newLeft, newRight)
}
var i = left
var j = right
array.Swap(left, k)
// in the original algorithm array[k] is stored to a value. To use golangs sort interface we need to keep track of the changes for the index
// we define it as right because in the first iteration of for i<j it will be changed
pointIndex := right
if array.Less(left, right) {
array.Swap(left, right)
pointIndex = left
}
for i < j {
// pointIndex is swapped only once in the first iteration. Later it will either be bigger (if left) or smaller (if right)
array.Swap(i, j)
i++
j--
for i < length && array.Less(i, pointIndex) {
i++
}
for j >= 0 && array.Less(pointIndex, j) {
j--
}
}
if !array.Less(left, pointIndex) && !array.Less(pointIndex, left) {
array.Swap(left, j)
} else {
j++
array.Swap(j, right)
}
if j <= k {
left = j + 1
}
if k <= j {
right = j - 1
}
}
}
func xSorterMin(a, b int) int {
if a < b {
return a
}
return b
}
func xSorterMax(a, b int) int {
if a > b {
return a
}
return b
}
type xSorterStack []int
func (s xSorterStack) push(v int) xSorterStack {
return append(s, v)
}
func (s xSorterStack) pop() (xSorterStack, int) {
l := len(s)
return s[:l-1], s[l-1]
}