From f15cc423d2b176a7935943e721917a2fca8bbe32 Mon Sep 17 00:00:00 2001 From: Richard Thier Date: Sat, 5 Apr 2025 03:35:21 +0200 Subject: [PATCH] minmax partitioning - maybe should support avg or dual-output-minmaxing too? --- qsort.h | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ zssort.h | 26 ++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/qsort.h b/qsort.h index 334e5e9..4f5c513 100644 --- a/qsort.h +++ b/qsort.h @@ -28,6 +28,7 @@ static inline void swapit(uint32_t *a, uint32_t *b) { * @param array The array to partition * @param low From when. (inclusive) * @param high Until when. (inclusive too!) + * @returns The partition point. */ static inline int partition(uint32_t array[], int low, int high) { /* This is "Lomuto"s unidirectional partitioner - see algorithms book */ @@ -85,6 +86,7 @@ static inline void quicksort(uint32_t array[], int low, int high) { * @param pi The index of the pivot element to use. 0 or high is what OG quicksorts do. * @param low From when. (inclusive) * @param high Until when. (inclusive too!) + * @returns The partition point. */ static inline int partition_with_pivot(uint32_t array[], int pi, int low, int high) { /* @@ -100,6 +102,85 @@ static inline int partition_with_pivot(uint32_t array[], int pi, int low, int hi return partition(array, low, high); } +/** + * Partition the array while doing a min-max search and find the pivot element such that + * + * - Elements smaller than pivot are on left of pivot + * - Elements greater than pivot are on right of pivot + * + * @param array The array to partition + * @param low From when. (inclusive) + * @param high Until when. (inclusive too!) + * @param minout OUT: Will be filled with the minimum key + * @param maxout OUT: Will be filled with the maximum key + * @returns The partition point. + */ +static inline int partition_and_minmax(uint32_t array[], int low, int high, uint32_t *minout, uint32_t *maxout) { + /* This is "Lomuto"s unidirectional partitioner - see algorithms book */ + + /* select the rightmost element as pivot */ + uint32_t pivot = array[high]; + *minout = pivot; + *maxout = pivot; + + /* index until smaller or eq elements lay */ + int i = (low - 1); + + /* traverse each element of the array */ + /* compare them with the pivot */ + #pragma GCC unroll 4 + for (int j = low; j < high; ++j) { + /* Branchless min-max */ + *minout = array[j] < *minout ? array[j] : *minout; + *maxout = array[j] > *maxout ? array[j] : *maxout; + + /* Lomuto partitioning */ + if (array[j] <= pivot) { + + /* if element smaller than pivot is found */ + /* swap it with the greater element pointed by i */ + ++i; + + /* swap element at i with element at j */ + swapit(&array[i], &array[j]); + } + } + + /* swap the pivot element with the greater element at i */ + swapit(&array[i + 1], &array[high]); + + /* return the partition point */ + return (i + 1); +} + +/** + * Partition the array and min-max and using the pivot index + * + * - Elements smaller than pivot are on left of pivot + * - Elements greater than pivot are on right of pivot + * + * @param array The array to partition + * @param pi The index of the pivot element to use. 0 or high is what OG quicksorts do. + * @param low From when. (inclusive) + * @param high Until when. (inclusive too!) + * @param minout OUT: Will be filled with the minimum key + * @param maxout OUT: Will be filled with the maximum key + * @returns The partition point. + */ +static inline int partition_and_minmax_with_pivot(uint32_t array[], int pi, int low, int high, uint32_t *minout, uint32_t *maxout) { + /* + * Rem.: This looks like overhead, + * but after seriously considering + * writing the whole out I can tell + * this is still fastests basically. + */ + + /* swap pivot with rightmost */ + swapit(&array[high], &array[pi]); + /* delegate to previous sol. */ + return partition_and_minmax(array, low, high, minout, maxout); +} + // 32-bit LCG for fast random generations static inline uint32_t lcg(uint32_t *state) { *state = *state * 1664525u + 1013904223u; diff --git a/zssort.h b/zssort.h index b4c4989..1070e16 100644 --- a/zssort.h +++ b/zssort.h @@ -4,6 +4,7 @@ #include #include "qsort.h" +/** 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) { @@ -27,6 +28,7 @@ static inline void zssort(uint32_t array[], int low, int high) { } } +/** 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; @@ -50,4 +52,28 @@ static inline void zssort_rand(uint32_t array[], int low, int high, rpivotstate } } +/** 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); + } + } +} + +// 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: Guess the pivot value by previous min-maxes and let us use it inn partitioning (which would lead to "closest to pivot value" being swapped to its right position to keep the left:pivot:right separation architecture. Maybe can be faster than rand, although our randoms are pretty darn fast as you can see. + +// TODO: Hackzolt éjféli ötlet: +// - Particionálás min-max-avgleft-el +// - két irányba rekurzió, bal oldalinak avgleft átadása, másik sima zssort_rand +// - avgleft-es rekurzióból visszahívás ennek a jelenleginek a fő függvényére (ide-oda típusú rekurzió!) +// - esetleg ha megéri, akkor avgright kiszámítása is jó lehet: drágább műveletek, de jobb algoritmus... hmmm + #endif /* ZS_SORT_H */