-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathintrosort.h
168 lines (140 loc) · 6.09 KB
/
introsort.h
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#ifndef INTROSORT_INTRO_SORT_H
#define INTROSORT_INTRO_SORT_H
#include <utility>
#include <random>
#include <algorithm>
#include <cassert>
#include <vector>
#include "sorting_networks.h"
#include "insertionsort.h"
template <typename Iterator>
void introsort(Iterator begin, Iterator end);
namespace introsort_implementation {
constexpr int CUT_OFF = 25;
constexpr int MAX_DIST = 15;
template<typename RandomIterator, typename Comparator>
typename std::iterator_traits<RandomIterator>::value_type
choose_pivot(RandomIterator low, RandomIterator high, Comparator comp) {
static std::random_device rd;
static std::mt19937 mt(rd());
static std::uniform_int_distribution<> distribution(0, std::numeric_limits<int>::max());
const auto length = high - low + 1;
typename std::iterator_traits<RandomIterator>::value_type x = *(low + distribution(mt) % length);
typename std::iterator_traits<RandomIterator>::value_type y = *(low + distribution(mt) % length);
typename std::iterator_traits<RandomIterator>::value_type z = *(low + distribution(mt) % length);
sort3(x, y, z, comp);
return y;
}
template<typename RandomIterator, typename Comparator>
std::pair<RandomIterator, RandomIterator>
three_way_partition(RandomIterator low, RandomIterator high, Comparator comp) {
assert(high - low + 1 >= 7);
RandomIterator lt = low;
RandomIterator gt = high;
auto pivot = choose_pivot(low, high, comp);
auto i = low;
while (i <= gt) {
if (comp(*i, pivot))
std::iter_swap(lt++, i++);
else if (comp(pivot, *i))
std::iter_swap(i, gt--);
else
i++;
}
return std::make_pair(lt, gt);
}
template<typename RandomIterator, typename Comparator>
void impl_intro_sort(RandomIterator low, RandomIterator high, unsigned depth, Comparator comp) {
while (low <= high) {
const auto length = high - low + 1;
/* Trivial case */
if (length <= 1)
return;
/* For very small ranges (< 7 elements) we use a sorting network */
switch (length) {
case 2:
sort2(*low, *(low + 1), comp);
return;
case 3:
sort3(*low, *(low + 1), *(low + 2), comp);
return;
case 4:
sort4(*low, *(low + 1), *(low + 2), *(low + 3), comp);
return;
case 5:
sort5(*low, *(low + 1), *(low + 2), *(low + 3), *(low + 4), comp);
return;
case 6:
sort6(*low, *(low + 1), *(low + 2), *(low + 3), *(low + 4), *(low + 5), comp);
return;
default:
break;
}
/* We have less than CUT_OFF elements so we must use insertion sort */
if (length <= CUT_OFF) {
insertion_sort(low, high + 1, comp);
return;
}
/* Quicksort is not behaving properly so we switch to heapsort */
if (depth == 0) {
std::make_heap(low, high + 1, comp);
std::sort_heap(low, high + 1, comp);
return;
}
// try gambling ? (optimization)
if (insertion_sort(low, high + 1, comp, MAX_DIST))
return;
/*
* We use 3-way partition in order to get 3 ranges:
* a[low..pit.first) ++ a[pit.first..pit.second] ++ a(pit.second..high]
* < pivot == pivot > pivot
*/
auto pit = three_way_partition(low, high, comp);
/*
* If len(a[low..pit.first)) < len(a(pit.second..high]) then
* transform sort(a(pit.second..high]) into tail recursion
* Otherwise transform sort(a[low..pit.first)) into tail recursion
*/
if (pit.first - low < high - pit.second){
impl_intro_sort(low, pit.first - 1, depth - 1, comp);
low = pit.second + 1;
}
else{
impl_intro_sort(pit.second + 1, high, depth - 1, comp);
high = pit.first - 1;
}
depth -= 1;
}
};
template<typename RandomIterator>
unsigned find_maximal_depth(RandomIterator low, RandomIterator high) {
return (sizeof(int) * __CHAR_BIT__ - 1 - __builtin_clz(high - low)) * 2;
}
template <typename RandomIterator>
void introsort(RandomIterator begin, RandomIterator end, std::random_access_iterator_tag){
if (begin != end)
impl_intro_sort(begin, --end, find_maximal_depth(begin, end),
std::less<typename std::iterator_traits<RandomIterator>::value_type>());
};
template <typename BidirectionalIterator>
void introsort(BidirectionalIterator begin, BidirectionalIterator end, std::bidirectional_iterator_tag){
std::vector<typename std::iterator_traits<BidirectionalIterator>::value_type> container(begin, end);
introsort(container.begin(), container.end(), std::random_access_iterator_tag());
std::move(container.begin(), container.end(), begin);
};
template <typename ForwardIterator>
void introsort(ForwardIterator begin, ForwardIterator end, std::forward_iterator_tag){
std::vector<typename std::iterator_traits<ForwardIterator>::value_type> container(begin, end);
introsort(container.begin(), container.end(), std::random_access_iterator_tag());
std::move(container.begin(), container.end(), begin);
};
template <typename Iterator>
void introsort(Iterator begin, Iterator end){
introsort_implementation::introsort(begin, end, typename std::iterator_traits<Iterator>::iterator_category());
};
}
template <typename Iterator>
void introsort(Iterator begin, Iterator end){
introsort_implementation::introsort(begin, end);
};
#endif //INTROSORT_INTRO_SORT_H