@@ -171,10 +171,11 @@ where
171
171
}
172
172
self [ i] . clone ( )
173
173
} 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.
176
175
let mut sample = [ 0 ; 5 ] ;
177
176
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.
178
179
let sought_rank = i as f64 / n as f64 ;
179
180
if ( 0.036 ..=0.964 ) . contains ( & sought_rank) {
180
181
let ( lower_index, upper_index) = if sought_rank <= 0.5 {
@@ -400,11 +401,80 @@ fn _get_many_from_sorted_mut_unchecked<A>(
400
401
return ;
401
402
}
402
403
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.
405
405
let mut sample = [ 0 ; 5 ] ;
406
406
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
+ } ;
408
478
409
479
// We partition the array with respect to the two pivot values. The pivot values move to the new
410
480
// `lower_index` and `upper_index`.
@@ -414,8 +484,9 @@ fn _get_many_from_sorted_mut_unchecked<A>(
414
484
// Elements greater than or equal the lower pivot value and less than or equal the upper pivot
415
485
// value have indexes > `lower_index` and < `upper_index`.
416
486
//
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] ) ;
419
490
420
491
// We use a divide-and-conquer strategy, splitting the indexes we are searching for (`indexes`)
421
492
// and the corresponding portions of the output slice (`values`) into partitions with respect to
0 commit comments