neoqs - that adds smallrange support for meanqs

This commit is contained in:
Richard Thier 2025-05-06 03:04:57 +02:00
parent bcc4f3bb62
commit d80a061240

123
zssort.h
View File

@ -1,5 +1,11 @@
#ifndef ZS_SORT_H #ifndef ZS_SORT_H
#define ZS_SORT_H #define ZS_SORT_H
/** Used in neoqs for applying smallrange heuristics below value-domains of this size */
#ifndef NEOQS_SMALLRANGE_TRESHOLD
#define NEOQS_SMALLRANGE_TRESHOLD 256
#endif
/* /*
* Structure: * Structure:
* - ZSSORT: The at most log(n) space needing quicksort * - ZSSORT: The at most log(n) space needing quicksort
@ -268,6 +274,123 @@ static inline void meanqs(uint32_t array[], int low, int high, rpivotstate *stat
} }
} }
/* NEOQS */
/**
* Neoquicksort entry point and outer-recursive function.
*
* We pick a random pivot, but in the meantime try to quess mean for next level partitioning.
* This and neoqs_inner is co-recursive (call each other)
* We utilize zsr3_sp2 in case the range is smaller than NEOQS_SMALLRANGE_TRESHOLD.
*
* @param array The array to sort
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param state The random pivot picking state
*/
static inline void neoqs(uint32_t array[], int low, int high, rpivotstate *state);
/**
* Inner-recursive function of neoqs - calls neoqs() as next level recursion.
*
* @param array The array to sort
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param pivotval We first partition to smaller-eq/bigger than pivotval, later we might pick random though
* @param state The random pivot picking state
*/
static inline void neoqs_inter(uint32_t array[], int low, int high, uint32_t pivotval, rpivotstate *state) {
/* First: partition using pivot val + recurse */
if (low < high) {
/* For the first level, use the pivotval they provide */
/* This only helps somewhat, because below loop.... */
/* ..But code it better if you can do consistently! */
int pi = partition_with_pivotval(array, low, high, pivotval);
/* If we recurse only the smaller part */
/* That ensures at most n/2 elements can */
/* be on any given level of the recursion */
/* tree: that is we ensure log2(N) memuse! */
if((pi - low) < (high - pi)) {
// Left smaller: recurse left of pivot
neoqs(array, low, pi - 1, state);
// (*) Update partitioning loop for remaining part
low = pi + 1;
} else {
// Right smaller: recurse right of pivot
neoqs(array, pi + 1, high, state);
// (*) Update partitioning loop for remaining part
high = pi - 1; /* high inclusive! */
}
}
/* Later: picking random pivot and partition like that */
/* (*) Rem.: The above if can be understood as different first iteration of this while loop... */
while (low < high) {
/* Random picking here - no info anymore about mean */
int pi = pick_pivot(state, (high + 1) - low) + low;
pi = partition_with_pivot(array, pi, low, high);
/* If we recurse only the smaller part */
/* That ensures at most n/2 elements can */
/* be on any given level of the recursion */
/* tree: that is we ensure log2(N) memuse! */
if((pi - low) < (high - pi)) {
// Left smaller: recurse left of pivot
neoqs(array, low, pi - 1, state);
// (*) Update partitioning loop for remaining part
low = pi + 1;
} else {
// Right smaller: recurse right of pivot
neoqs(array, pi + 1, high, state);
// (*) Update partitioning loop for remaining part
high = pi - 1; /* high inclusive! */
}
}
}
/**
* Neoquicksort entry point and outer-recursive function.
*
* We pick a random pivot, but in the meantime try to guess mean for next level partitioning.
* This and neoqs_inner is co-recursive (call each other)
* We utilize zsr3_sp2 in case the range is smaller than NEOQS_SMALLRANGE_TRESHOLD.
*
* @param array The array to sort
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param state The random pivot picking state
*/
static inline void neoqs(uint32_t array[], int low, int high, rpivotstate *state) {
if (low < high) {
uint32_t lmin, lmax, rmin, rmax;
int pi = pick_pivot(state, (high + 1) - low) + low;
pi = partition_and_pminmax_with_pivot(array, pi, low, high, &lmin, &lmax, &rmin, &rmax);
/* These also handle constant (always same valued) subarrays */
if(lmin != lmax) {
/* Better than (a+b)/2 avg because of overflow possibilities */
uint32_t lavg = lmin + (lmax - lmin) / 2;
/* Changing to zzsort_rand3_sp2 ensures better smallranges */
/* Recursion with inter "ensures" logn stack spacen need! */
if(lmax - lmin < NEOQS_SMALLRANGE_TRESHOLD) zssort_rand3_sp2(array, low, pi - 1, state);
else neoqs_inter(array, low, pi - 1, lavg, state);
}
if(rmin != rmax) {
/* Better than (a+b)/2 avg because of overflow possibilities */
uint32_t ravg = rmin + (rmax - rmin) / 2;
/* Changing to zzsort_rand3_sp2 ensures better smallranges */
/* Recursion with inter "ensures" logn stack spacen need! */
if(rmax - rmin < NEOQS_SMALLRANGE_TRESHOLD) zssort_rand3_sp2(array, pi + 1, high, state);
else neoqs_inter(array, pi + 1, high, ravg, state);
}
}
}
// TODO: Idea: Quadratic time happens when you repeatedly pick pivots close to the maximum or minimum - so why not re-pick near extremals being picked which now can be cheked? I think we should have a treshold - maybe dynamically set by first minmax - and logarithmically divide the treshold by 2 in case its still too close the next randomization time. This handles smallrange badly though. // TODO: Idea: Quadratic time happens when you repeatedly pick pivots close to the maximum or minimum - so why not re-pick near extremals being picked which now can be cheked? I think we should have a treshold - maybe dynamically set by first minmax - and logarithmically divide the treshold by 2 in case its still too close the next randomization time. This handles smallrange badly though.
#endif /* ZS_SORT_H */ #endif /* ZS_SORT_H */