221 lines
7.8 KiB
C
221 lines
7.8 KiB
C
#ifndef ZS_SORT_H
|
|
#define ZS_SORT_H
|
|
/*
|
|
* Structure:
|
|
* - ZSSORT: The at most log(n) space needing quicksort
|
|
* - RZSSORT: Randomized pivoting variant at most log(n) space needing quicksort
|
|
* - RZSSORTC: Added check for fully same value const array (also randomized pivot)
|
|
* - MEANQS: Randomized pivoting AND pivoting based on min-max range - dual recursive
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include "qsort.h"
|
|
|
|
/* ZSSORT */
|
|
|
|
/** Always at most log(n) space needing quicksort variant */
|
|
static inline void zssort(uint32_t array[], int low, int high) {
|
|
/* (*) Loop handles original "other half recursion"! */
|
|
while(low < high) {
|
|
int pi = partition(array, 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
|
|
zssort(array, low, pi - 1);
|
|
// (*) Update partitioning loop for remaining part
|
|
low = pi + 1;
|
|
} else {
|
|
// Right smaller: recurse right of pivot
|
|
zssort(array, pi + 1, high);
|
|
// (*) Update partitioning loop for remaining part
|
|
high = pi - 1; /* high inclusive! */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ZSSORTR */
|
|
|
|
/** Always at most log(n) space needing randomized quicksort variant */
|
|
static inline void zssort_rand(uint32_t array[], int low, int high, rpivotstate *state) {
|
|
while (low < high) {
|
|
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
|
|
zssort_rand(array, low, pi - 1, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
low = pi + 1;
|
|
} else {
|
|
// Right smaller: recurse right of pivot
|
|
zssort_rand(array, pi + 1, high, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
high = pi - 1; /* high inclusive! */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* THREEWAY_ZS */
|
|
|
|
/** Always at most log(n) space needing randomized quicksort variant */
|
|
static inline void zssort_rand3(uint32_t array[], int low, int high, rpivotstate *state) {
|
|
while (low < high) {
|
|
int pi = pick_pivot(state, (high + 1) - low) + low;
|
|
pret3 res = partition3(array, low, high, array[pi]);
|
|
|
|
/* 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((res.leftend - low) < (high - res.rightend)) {
|
|
// Left smaller: recurse left of pivot
|
|
zssort_rand3(array, low, res.leftend, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
low = res.rightend;
|
|
} else {
|
|
// Right smaller: recurse right of pivot
|
|
zssort_rand3(array, res.rightend, high, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
high = res.leftend; /* high inclusive! */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ZSSORTC */
|
|
|
|
/** Always at most log(n) space needing randomized quicksort variant - with checking for sameconst-arrays */
|
|
static inline void zsrc(uint32_t array[], int low, int high, rpivotstate *state) {
|
|
if (low < high) {
|
|
uint32_t min, max;
|
|
int pi = pick_pivot(state, (high + 1) - low) + low;
|
|
pi = partition_and_minmax_with_pivot(array, pi, low, high, &min, &max);
|
|
if(min != max) {
|
|
/* Recursion with zssort_rand ensures logn+1 stack spacen need! */
|
|
zssort_rand(array, low, pi - 1, state);
|
|
zssort_rand(array, pi + 1, high, state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* MEANQS */
|
|
|
|
/**
|
|
* Rangsort entry point and outer-recursive function of meanqs.
|
|
*
|
|
* We pick a random pivot, but in the meantime try to quess mean for next level partitioning.
|
|
* This and meanqs_inner is co-recursive (call each other)
|
|
*
|
|
* @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 meanqs(uint32_t array[], int low, int high, rpivotstate *state);
|
|
|
|
/**
|
|
* Inner-recursive function of meanqs - calls meanqs() 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 meanqs_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
|
|
meanqs(array, low, pi - 1, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
low = pi + 1;
|
|
} else {
|
|
// Right smaller: recurse right of pivot
|
|
meanqs(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
|
|
meanqs(array, low, pi - 1, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
low = pi + 1;
|
|
} else {
|
|
// Right smaller: recurse right of pivot
|
|
meanqs(array, pi + 1, high, state);
|
|
// (*) Update partitioning loop for remaining part
|
|
high = pi - 1; /* high inclusive! */
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rangsort entry point and outer-recursive function of meanqs.
|
|
*
|
|
* We pick a random pivot, but in the meantime try to quess mean for next level partitioning.
|
|
* This and meanqs_inner is co-recursive (call each other)
|
|
*
|
|
* @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 meanqs(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;
|
|
/* Recursion with inter "ensures" logn stack spacen need! */
|
|
meanqs_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;
|
|
/* Recursion with inter "ensures" logn stack spacen need! */
|
|
meanqs_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.
|
|
|
|
#endif /* ZS_SORT_H */
|