Skip to main content

a/sort/
selection.rs

1use core::cmp::Ordering::{self, Equal, Greater, Less};
2use core::hint::select_unpredictable;
3use core::mem;
4
5/// Sorts a slice in place using double-ended selection sort.
6///
7/// Each pass scans the unsorted middle of the slice once, finds both its smallest and its
8/// largest key, moves every element holding the smallest key to the front of that range and
9/// every element holding the largest key to its back. Those elements are then in their final
10/// places, so the next pass scans only what lies between them.
11///
12/// Three refinements keep the algorithm as cheap as selection sort can be:
13///
14/// * **Pairwise min-max scan.** Elements are taken two at a time: the two are compared with
15///   each other first, then only the smaller one is compared with the running minimum and
16///   only the larger one with the running maximum. A range of `m` elements costs
17///   `⌈3m/2⌉ - 2` comparisons per pass instead of the `2m - 2` of two independent scans,
18///   which is the fewest any algorithm can use to find both extremes.
19/// * **Duplicate gathering ("bingo") for free.** Every comparison is a three-way
20///   [`Ord::cmp`], so an element equal to the running minimum or maximum is recognised
21///   without an extra comparison and joins that key's group. A pass therefore retires all
22///   copies of two keys: input with `k` distinct keys finishes in at most `⌈k/2⌉` passes
23///   instead of `⌊n/2⌋`. A pass in which every comparison reports [`Equal`] has proved the whole range
24///   equal and ends the sort, again at no extra cost. An equal pair also skips the
25///   comparison with the maximum whenever its minimum comparison already rules that out.
26/// * **Lazy placement.** A running minimum or maximum that has no duplicate yet is only
27///   remembered by index; elements are moved once a pass is over, or when a second copy of
28///   the current extreme turns up. Input with distinct keys costs at most `n - 1` swaps,
29///   exactly like classic selection sort, and gathering duplicates never costs a comparison.
30///
31/// Which element of a pair is the smaller one is a coin flip on random input, and a branch on
32/// it would be mispredicted every other pair. The scan therefore picks the element to compare
33/// with the minimum, and the one to compare with the maximum, without branching, and only
34/// branches on the rare outcomes that change an extreme: on random input with distinct keys
35/// that is 1.1–1.5 times faster at 64 elements and 1.7–2.5 times from 1,000 up, for integers,
36/// strings and 64-byte arrays alike. Choosing without a branch makes the next two comparisons
37/// wait for the first, which costs more than a predicted branch when comparisons are slow to
38/// start, as for strings in order: so for elements of 9 to 32 bytes the first pass branches
39/// and counts how often a pair goes against the majority order, and later passes branch only
40/// if fewer than one in 16 did. The comparisons are the same either way.
41///
42/// Distinct keys get no benefit from the refinements beyond the pairwise scan, so the worst
43/// case is any input with distinct keys, and on such input the number of comparisons
44/// depends only on `n`: `n(3n - 2)/8` for even `n` and `3(n² - 1)/8` for odd `n`, about 25%
45/// fewer than the `n(n - 1)/2` of classic selection sort.
46///
47/// # Properties
48///
49/// * **Not stable**: equal elements may be reordered.
50/// * **In place**: `O(1)` auxiliary memory, no allocation.
51/// * **Not adaptive to order**: sorted input costs as much as any other with distinct keys.
52///   It is adaptive to duplicates instead: the fewer distinct keys, the fewer passes.
53///
54/// | Case                          | Comparisons                         | Swaps          |
55/// |-------------------------------|-------------------------------------|----------------|
56/// | Best (all elements equal)     | `n - 1`                             | `0`            |
57/// | `k` distinct keys             | `O(nk)`                             | `O(nk)`        |
58/// | Worst (all keys distinct)     | `n(3n - 2)/8` or `3(n² - 1)/8`      | `≤ n - 1`      |
59///
60/// Gathering duplicates costs at most `2m` swaps in a pass over `m` elements, so input with
61/// many repeated keys trades the `n - 1` swap bound for far fewer comparisons and passes.
62/// Input that is already in non-decreasing order is never moved at all.
63///
64/// Selection sort is quadratic and is mainly of educational value; prefer
65/// [`slice::sort_unstable`] for real workloads.
66///
67/// # Panics
68///
69/// Does not panic by itself. If the [`Ord`] implementation of `T` panics, the panic is
70/// propagated and `arr` is left in an unspecified order, but it still holds every original
71/// element exactly once: elements are only ever exchanged with [`slice::swap`].
72///
73/// # Examples
74///
75/// ```
76/// use a::sort::selection;
77///
78/// let mut v = [5, 1, 4, 2, 8, 0];
79/// selection(&mut v);
80/// assert_eq!(v, [0, 1, 2, 4, 5, 8]);
81///
82/// // Three distinct keys: the first pass retires every 1 and every 3, the second the 2s.
83/// let mut v = [3, 1, 2, 3, 1, 2, 2, 1];
84/// selection(&mut v);
85/// assert_eq!(v, [1, 1, 1, 2, 2, 2, 3, 3]);
86///
87/// let mut words = vec!["pear", "apple", "fig"];
88/// selection(&mut words);
89/// assert_eq!(words, ["apple", "fig", "pear"]);
90/// ```
91pub fn selection<T: Ord>(arr: &mut [T]) {
92    // Everything before `lo` and from `hi` onwards is already in its final position.
93    let (mut lo, mut hi) = (0, arr.len());
94    // Whether to branch on the order within a pair: decided by the first pass for elements of
95    // 9 to 32 bytes, never otherwise. Passes see the same middle of the slice, minus the
96    // extremes, so the first one tells how predictable the others will be.
97    let size = mem::size_of::<T>();
98    let mut first = size > 8 && size <= 32;
99    let mut branchy = false;
100    while hi - lo > 1 {
101        let mut pass = Pass::new(&mut arr[lo..hi]);
102        if first {
103            let (pairs, flips) = pass.scan::<true, true>();
104            branchy = flips * FLIPS_MAX < pairs;
105            first = false;
106        } else if branchy {
107            pass.scan::<true, false>();
108        } else {
109            pass.scan::<false, false>();
110        }
111        match pass.finish() {
112            Some((front, back)) => {
113                lo += front;
114                hi -= back;
115            }
116            None => return, // every element still unsorted is equal to every other
117        }
118    }
119}
120
121/// The first pass decides to branch on the order within pairs if fewer than one pair in
122/// `FLIPS_MAX` goes against the majority order.
123const FLIPS_MAX: usize = 16;
124
125/// One pass of [`selection`] over the unsorted range `v`.
126///
127/// The scanned prefix `v[..end]` holds the elements equal to the running minimum (the min
128/// group), the elements equal to the running maximum (the max group), and everything else in
129/// no particular order. A group with a single element is only remembered by its index; once
130/// it has two or more, it is kept contiguous:
131///
132/// * the min group occupies `v[..min_len]`, at the front, where it has to end up anyway;
133/// * the max group occupies `v[mb..mb + max_len]`, somewhere after the min group.
134///
135/// While every element scanned so far is equal (`same`), they all belong to the min group and
136/// the max fields are unused.
137struct Pass<'a, T> {
138    v: &'a mut [T],
139    /// Number of elements scanned.
140    end: usize,
141    /// Whether every element scanned so far is equal.
142    same: bool,
143    /// Length of the contiguous min group at the front, or `0` while it is a single element.
144    min_len: usize,
145    /// Index of an element of the min group.
146    mi: usize,
147    /// Length of the contiguous max group, or `0` while it is a single element.
148    max_len: usize,
149    /// Start of the contiguous max group, when there is one.
150    mb: usize,
151    /// Index of an element of the max group.
152    mx: usize,
153    /// Indices of elements still being placed, kept up to date by [`Pass::swap`].
154    held: [usize; 2],
155}
156
157impl<'a, T: Ord> Pass<'a, T> {
158    fn new(v: &'a mut [T]) -> Self {
159        Pass {
160            v,
161            end: 0,
162            same: true,
163            min_len: 0,
164            mi: 0,
165            max_len: 0,
166            mb: 0,
167            mx: 0,
168            held: [0; 2],
169        }
170    }
171
172    /// Scans the whole range, gathering the min and max groups.
173    ///
174    /// With `BRANCHY`, branches on which element of a pair is the smaller one; otherwise
175    /// selects it without a branch. With `COUNT`, returns the number of pairs scanned after
176    /// the first and how many of them went against the majority order.
177    #[inline(always)]
178    fn scan<const BRANCHY: bool, const COUNT: bool>(&mut self) -> (usize, usize) {
179        let len = self.v.len();
180        // An odd length starts from a lone element, an even one from a compared pair, so
181        // the rest of the range always splits into whole pairs.
182        if len % 2 == 1 {
183            self.end = 1;
184        } else {
185            self.end = 2;
186            match self.v[0].cmp(&self.v[1]) {
187                Less => {
188                    self.same = false;
189                    self.mx = 1;
190                }
191                Greater => {
192                    self.same = false;
193                    self.mi = 1;
194                }
195                Equal => self.push_min(1),
196            }
197        }
198        let start = self.end;
199        let mut greater = 0;
200        while self.end < len {
201            let x = self.end;
202            self.end += 2;
203            let y = x + 1;
204            // SAFETY: the rest of the range splits into whole pairs, so `y < len`.
205            let order = unsafe { self.v.get_unchecked(x).cmp(self.v.get_unchecked(y)) };
206            if order == Equal {
207                self.pair_equal(x, y);
208                continue;
209            }
210            let less = order == Less;
211            let (s, l) = if BRANCHY {
212                if less {
213                    (x, y)
214                } else {
215                    if COUNT {
216                        greater += 1;
217                    }
218                    (y, x)
219                }
220            } else {
221                if COUNT {
222                    greater += usize::from(!less);
223                }
224                select_unpredictable(less, (x, y), (y, x))
225            };
226            // Before the first unequal pair, the minimum is also the maximum. Both
227            // comparisons are made against the extremes as they were before the pair.
228            let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
229            // SAFETY: `s` and `l` are `x` and `y`, and `mi` and `mx` always index scanned
230            // elements.
231            let (below, above) = unsafe {
232                (
233                    self.v.get_unchecked(s).cmp(self.v.get_unchecked(min)),
234                    self.v.get_unchecked(l).cmp(self.v.get_unchecked(max)),
235                )
236            };
237            if below == Greater && above == Less {
238                // Both lie strictly between the extremes: by far the most common case.
239                continue;
240            }
241            self.place(s, l, below, above);
242        }
243        let pairs = (len - start) / 2;
244        (pairs, greater.min(pairs - greater))
245    }
246
247    /// Places an equal pair at `x` and `y` with at most two more comparisons.
248    fn pair_equal(&mut self, x: usize, y: usize) {
249        // Before the first unequal pair, the minimum is also the maximum.
250        let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
251        // Both elements share one key, so one comparison against the minimum often
252        // settles both of them.
253        self.held[0] = y;
254        let joined_min = match self.v[x].cmp(&self.v[min]) {
255            Less => {
256                self.new_min(x);
257                true
258            }
259            Equal => {
260                self.push_min(x);
261                true
262            }
263            // Greater than the minimum while everything so far is equal, so it is
264            // also greater than the maximum.
265            Greater if self.same => {
266                self.new_max(x);
267                false
268            }
269            Greater => match self.v[x].cmp(&self.v[max]) {
270                Less => return, // both lie strictly between the extremes
271                Equal => {
272                    self.push_max(x);
273                    false
274                }
275                Greater => {
276                    self.new_max(x);
277                    false
278                }
279            },
280        };
281        let y = self.held[0];
282        if joined_min {
283            self.push_min(y);
284        } else {
285            self.push_max(y);
286        }
287    }
288
289    /// Places an unequal pair, its smaller element at `s` and its larger at `l`, given how
290    /// they compared with the old minimum (`below`) and the old maximum (`above`): the smaller
291    /// element is placed before the larger one.
292    fn place(&mut self, s: usize, l: usize, below: Ordering, above: Ordering) {
293        self.held[0] = l;
294        match below {
295            Less => self.new_min(s),
296            Equal => self.push_min(s),
297            Greater => {}
298        }
299        let l = self.held[0];
300        match above {
301            Less => {}
302            // `l` equals the old maximum. If that was also the old minimum, `s` has
303            // just undercut it, which turned the old min group into the max group.
304            Equal => self.push_max(l),
305            Greater => self.new_max(l),
306        }
307    }
308
309    /// Makes the element at `src` the new minimum; the old min group becomes ordinary.
310    fn new_min(&mut self, src: usize) {
311        if self.same {
312            // Every element scanned so far was equal, and they are all larger than `src`:
313            // they form the max group now, exactly where they already are.
314            self.same = false;
315            self.max_len = self.min_len;
316            self.mb = 0;
317            self.mx = self.mi;
318        }
319        self.min_len = 0;
320        self.mi = src;
321    }
322
323    /// Makes the element at `src` the new maximum; the old max group becomes ordinary.
324    fn new_max(&mut self, src: usize) {
325        self.same = false;
326        self.max_len = 0;
327        self.mx = src;
328    }
329
330    /// Adds the element at `src`, which equals the minimum, to the min group.
331    fn push_min(&mut self, src: usize) {
332        self.held[1] = src;
333        if self.min_len == 0 {
334            // The minimum's only element so far opens the group at the front of the range.
335            self.make_room_for_min();
336            self.swap(0, self.mi);
337            self.min_len = 1;
338        }
339        self.make_room_for_min();
340        self.swap(self.min_len, self.held[1]);
341        self.min_len += 1;
342        self.mi = 0;
343    }
344
345    /// Frees the slot just after the min group if the max group starts there, by moving the
346    /// max group's first element to just past its end.
347    fn make_room_for_min(&mut self) {
348        // The element being added to the min group belongs to neither group, and it lies
349        // in the scanned prefix but not in front of the max group, so `mb + max_len` is
350        // still inside the scanned prefix.
351        if self.max_len > 0 && self.mb == self.min_len {
352            self.swap(self.mb, self.mb + self.max_len);
353            self.mb += 1;
354            self.mx = self.mb;
355        }
356    }
357
358    /// Adds the element at `src`, which equals the maximum, to the max group.
359    fn push_max(&mut self, src: usize) {
360        if self.max_len == 0 {
361            // The maximum's only element so far opens the group where it already is.
362            self.mb = self.mx;
363            self.max_len = 1;
364        }
365        // Grow the group to the right while that stays inside the scanned prefix. When it
366        // does not, the group ends the prefix and `src` lies before it but after the min
367        // group, so the slot just before the group is free to take.
368        let next = self.mb + self.max_len;
369        let slot = if next < self.end {
370            next
371        } else {
372            self.mb -= 1;
373            self.mb
374        };
375        self.swap(slot, src);
376        self.max_len += 1;
377        self.mx = self.mb;
378    }
379
380    /// Moves the groups into their final places and returns how many elements were placed at
381    /// the front and at the back, or `None` if the whole range turned out to be equal.
382    fn finish(&mut self) -> Option<(usize, usize)> {
383        if self.same {
384            return None;
385        }
386        let len = self.v.len();
387        // The max group goes first: the slot the minimum needs, `v[0]`, may lie inside it.
388        let back = if self.max_len == 0 {
389            self.swap(len - 1, self.mx);
390            1
391        } else {
392            // Only the group's elements outside `v[target..]` move, each into a slot of
393            // `v[target..]` that the group does not already occupy.
394            let (start, count) = (self.mb, self.max_len);
395            let target = len - count;
396            let free = target.max(start + count);
397            for k in 0..(target - start).min(count) {
398                self.swap(start + k, free + k);
399            }
400            count
401        };
402        let front = if self.min_len == 0 {
403            self.swap(0, self.mi);
404            1
405        } else {
406            self.min_len
407        };
408        Some((front, back))
409    }
410
411    /// Swaps two elements and keeps every remembered index pointing at the same element.
412    fn swap(&mut self, a: usize, b: usize) {
413        if a == b {
414            return;
415        }
416        self.v.swap(a, b);
417        let [h0, h1] = &mut self.held;
418        for index in [&mut self.mi, &mut self.mx, h0, h1] {
419            if *index == a {
420                *index = b;
421            } else if *index == b {
422                *index = a;
423            }
424        }
425    }
426}