Skip to content

Commit 2b1b502

Browse files
committed
Adapt pivot sampling for bulk version.
1 parent fc13897 commit 2b1b502

File tree

1 file changed

+78
-7
lines changed

1 file changed

+78
-7
lines changed

src/sort.rs

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,11 @@ where
171171
}
172172
self[i].clone()
173173
} else {
174-
// Adapt pivot sampling to relative sought rank and switch from dual-pivot to
175-
// single-pivot partitioning for extreme sought ranks.
174+
// Sorted sample of 5 equally spaced elements around the center.
176175
let mut sample = [0; 5];
177176
sample_mut(self, &mut sample);
177+
// Adapt pivot sampling to relative sought rank and switch from dual-pivot to
178+
// single-pivot partitioning for extreme sought ranks.
178179
let sought_rank = i as f64 / n as f64;
179180
if (0.036..=0.964).contains(&sought_rank) {
180181
let (lower_index, upper_index) = if sought_rank <= 0.5 {
@@ -400,11 +401,80 @@ fn _get_many_from_sorted_mut_unchecked<A>(
400401
return;
401402
}
402403

403-
// Since there is no single sought rank to adapt pivot sampling to, the recommended skewed pivot
404-
// sampling of dual-pivot Quicksort is used.
404+
// Sorted sample of 5 equally spaced elements around the center.
405405
let mut sample = [0; 5];
406406
sample_mut(&mut array, &mut sample);
407-
let (lower_index, upper_index) = (sample[0], sample[2]); // (0, 1, 2)
407+
let (lower_index, upper_index) = if indexes.len() == 1 {
408+
// Adapt pivot sampling to relative sought rank and switch from dual-pivot to single-pivot
409+
// partitioning for extreme sought ranks.
410+
let sought_rank = indexes[0] as f64 / n as f64;
411+
if (0.036..=0.964).contains(&sought_rank) {
412+
if sought_rank <= 0.5 {
413+
if sought_rank <= 0.153 {
414+
(0, 1) // (0, 0, 3)
415+
} else {
416+
(0, 2) // (0, 1, 2)
417+
}
418+
} else {
419+
if sought_rank <= 0.847 {
420+
(2, 4) // (2, 1, 0)
421+
} else {
422+
(3, 4) // (3, 0, 0)
423+
}
424+
}
425+
} else {
426+
let pivot_index = if sought_rank <= 0.5 {
427+
0 // (0, 4)
428+
} else {
429+
4 // (4, 0)
430+
};
431+
432+
// We partition the array with respect to the pivot value. The pivot value moves to the
433+
// new `pivot_index`.
434+
//
435+
// Elements strictly less than the pivot value have indexes < `pivot_index`.
436+
//
437+
// Elements greater than or equal the pivot value have indexes > `pivot_index`.
438+
let pivot_index = array.partition_mut(sample[pivot_index]);
439+
let (found_exact, split_index) = match indexes.binary_search(&pivot_index) {
440+
Ok(index) => (true, index),
441+
Err(index) => (false, index),
442+
};
443+
let (lower_indexes, upper_indexes) = indexes.split_at_mut(split_index);
444+
let (lower_values, upper_values) = values.split_at_mut(split_index);
445+
let (upper_indexes, upper_values) = if found_exact {
446+
upper_values[0] = array[pivot_index].clone(); // Write exactly found value.
447+
(&mut upper_indexes[1..], &mut upper_values[1..])
448+
} else {
449+
(upper_indexes, upper_values)
450+
};
451+
452+
// We search recursively for the values corresponding to indexes strictly less than
453+
// `pivot_index` in the lower partition.
454+
_get_many_from_sorted_mut_unchecked(
455+
array.slice_axis_mut(Axis(0), Slice::from(..pivot_index)),
456+
lower_indexes,
457+
lower_values,
458+
);
459+
460+
// We search recursively for the values corresponding to indexes greater than or equal
461+
// `pivot_index` in the upper partition. Since only the upper partition of the array is
462+
// passed in, the indexes need to be shifted by length of the lower partition.
463+
upper_indexes.iter_mut().for_each(|x| *x -= pivot_index + 1);
464+
_get_many_from_sorted_mut_unchecked(
465+
array.slice_axis_mut(Axis(0), Slice::from(pivot_index + 1..)),
466+
upper_indexes,
467+
upper_values,
468+
);
469+
470+
return;
471+
}
472+
} else {
473+
// Since there is no single sought rank to adapt pivot sampling to, the recommended skewed
474+
// pivot sampling of dual-pivot Quicksort is used in the assumption that multiple indexes
475+
// change characteristics from Quickselect towards Quicksort.
476+
(0, 2) // (0, 1, 2)
477+
};
408478

409479
// We partition the array with respect to the two pivot values. The pivot values move to the new
410480
// `lower_index` and `upper_index`.
@@ -414,8 +484,9 @@ fn _get_many_from_sorted_mut_unchecked<A>(
414484
// Elements greater than or equal the lower pivot value and less than or equal the upper pivot
415485
// value have indexes > `lower_index` and < `upper_index`.
416486
//
417-
// Elements less than or equal the upper pivot value have indexes > `upper_index`.
418-
let (lower_index, upper_index) = array.dual_partition_mut(lower_index, upper_index);
487+
// Elements greater than or equal the upper pivot value have indexes > `upper_index`.
488+
let (lower_index, upper_index) =
489+
array.dual_partition_mut(sample[lower_index], sample[upper_index]);
419490

420491
// We use a divide-and-conquer strategy, splitting the indexes we are searching for (`indexes`)
421492
// and the corresponding portions of the output slice (`values`) into partitions with respect to

0 commit comments

Comments
 (0)