added three-way single pass partitioning codes, they are a bit slower than the two-pass 3-way partitioning on my machine, but sp2 gets very close in performance so might be tweaked to get it be better maybe

This commit is contained in:
Richard Thier 2025-05-05 22:52:20 +02:00
parent 12d30a588a
commit 5d6ea0e778
3 changed files with 262 additions and 8 deletions

32
qs.c
View File

@ -72,10 +72,42 @@ void qs3() {
printArray(data, n); printArray(data, n);
} }
void qs3_sp() {
#include "data.inc"
int n = sizeof(data) / sizeof(data[0]);
// memory junnnk is enough
rpivotstate rand;
// perform qs3 on data
quicksort_rand3_sp(data, 0, n - 1, &rand);
printf("(qs3_sp) Sorted array in ascending order: \n");
printArray(data, n);
}
void qs3_sp2() {
#include "data.inc"
int n = sizeof(data) / sizeof(data[0]);
// memory junnnk is enough
rpivotstate rand;
// perform qs3 on data
quicksort_rand3_sp2(data, 0, n - 1, &rand);
printf("(qs3_sp2) Sorted array in ascending order: \n");
printArray(data, n);
}
int main() { int main() {
qs(); qs();
qsr(); qsr();
zss(); zss();
qs3(); qs3();
qs3_sp();
qs3_sp2();
return 0; return 0;
} }

185
qsort.h
View File

@ -2,6 +2,11 @@
#ifndef MY_QUICKSORT_H #ifndef MY_QUICKSORT_H
#define MY_QUICKSORT_H #define MY_QUICKSORT_H
/* Used in quicksort_rand3_sp under which we call regular quicksort_rand3 without singlepassing */
#ifndef RAND3_SP_VAL
#define RAND3_SP_VAL 64
#endif
#include <stdint.h> #include <stdint.h>
/* Uncomment for debugging with if(someevent) raise(SIGINT); */ /* Uncomment for debugging with if(someevent) raise(SIGINT); */
@ -97,22 +102,19 @@ typedef struct pret3 pret3;
* @returns The partition points, end of left and right (inclusive) * @returns The partition points, end of left and right (inclusive)
*/ */
static inline pret3 partition3(uint32_t array[], int low, int high, uint32_t pivotval) { static inline pret3 partition3(uint32_t array[], int low, int high, uint32_t pivotval) {
/* select the rightmost element as pivot */
uint32_t pivot = pivotval;
/* index until smaller or eq elements lay */ /* index until smaller or eq elements lay */
int i = (low - 1); int i = (low - 1);
uint32_t pc = 0; uint32_t pc = 0;
/* traverse each element of the array */ /* traverse each element of the array */
/* compare them with the pivot */ /* compare them with the pivotval */
#pragma GCC unroll 4 #pragma GCC unroll 4
for (int j = low; j <= high; ++j) { for (int j = low; j <= high; ++j) {
/* Branchless pivot-count */ /* Branchless pivot-count */
pc += (array[j] == pivot); pc += (array[j] == pivotval);
if(array[j] < pivot) { if(array[j] < pivotval) {
/* if element smaller than pivot is found */ /* if element smaller than pivotval is found */
/* swap it with the greater element pointed by i */ /* swap it with the greater element pointed by i */
++i; ++i;
@ -134,7 +136,7 @@ static inline pret3 partition3(uint32_t array[], int low, int high, uint32_t piv
#pragma GCC unroll 4 #pragma GCC unroll 4
for (int j = high; j > i; --j) { for (int j = high; j > i; --j) {
if (array[j] > pivot) { if (array[j] > pivotval) {
/* if element smaller than pivot is found */ /* if element smaller than pivot is found */
/* swap it with the greater element pointed by i */ /* swap it with the greater element pointed by i */
--i2; --i2;
@ -151,6 +153,138 @@ static inline pret3 partition3(uint32_t array[], int low, int high, uint32_t piv
return ret; return ret;
} }
/**
* Partition the array threeway by puutting ALL pivots to the middle and smaller/biggers left-right.
* Single-pass version.
*
* @param array The array to partition
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param pivotval This value is used to partition the array.
* @returns The partition points, end of left and right (inclusive)
*/
static inline pret3 partition3_sp(uint32_t array[], int low, int high, uint32_t pivotval) {
/* Invariant for left: index until smaller (than pivot) elements lay */
int il = (low - 1);
/* Invariant for right: index until (from top) bigger elements lay */
int ir = (high + 1);
/* Indices from where we swap left and right into "is" (and sometimes swap among here too) */
int jl = low;
int jr = high;
while(jl <= jr) {
/* Jump through any pivot elements */
while((array[jl] == pivotval) && (jl < high)) ++jl;
while((array[jr] == pivotval) && (jr > low)) --jr;
if(jl > jr) break;
if(array[jl] < pivotval) {
/* if element smaller than pivotval is found */
/* swap it with the element pointed by i */
++il;
if(il != jl) swapit(&array[il], &array[jl]);
} else {
/* Here its sure a[jl] and a[jr] are not pivots */
/* Which means array[jl] > pivotval and on left */
if(array[jr] < pivotval) {
/* We can totally safely just swap them */
/* Because both are in wrong places.... */
swapit(&array[jl], &array[jr]);
/* swap it with the element pointed by i */
++il;
if(il != jl) swapit(&array[il], &array[jl]);
} else {
/* Left->right move needed, but right is */
/* at good place already so handle that! */
/* ensured: if(array[jr] > pivotval) */
/* Progress invariants */
/* swap with the element pointed by i */
--ir;
if(ir != jr) swapit(&array[ir], &array[jr]);
/* Because of swap we might swaped in pivot! */
/* So continue our loop but do not step jl! */
/* ++jl; // incomplete so no step! */
--jr; /* STEP RIGHT because "continue"! */
continue;
}
}
/* Step left being processed */
++jl;
if(array[jr] > pivotval) {
/* if element bigger than pivotval is found */
/* swap it with the element pointed by i */
--ir;
if(ir != jr) swapit(&array[ir], &array[jr]);
} else {
/* On wrong side - so we just need to look */
/* for left-wrong-sided to swap it with! */
continue; /* I think no ++jl needed! */
}
/* Step right being processed */
--jr;
}
/* return the partition points */
pret3 ret;
ret.leftend = il;
ret.rightend = ir;
return ret;
}
/**
* Partition the array threeway by puutting ALL pivots to the middle and smaller/biggers left-right.
* Single-pass version 2.0
*
* @param array The array to partition
* @param low From when. (inclusive)
* @param high Until when. (inclusive too!)
* @param pivotval This value is used to partition the array.
* @returns The partition points, end of left and right (inclusive)
*/
static inline pret3 partition3_sp2(uint32_t array[], int low, int high, uint32_t pivotval) {
/* Invariant for left: index until smaller (than pivot) elements lay */
int il = (low - 1);
/* Invariant for right: index until (from top) bigger elements lay */
int ir = (high + 1);
/* Indices from where we swap left and right into "is" (and sometimes swap among here too) */
int jl = low;
int jr = high;
while(jl <= jr) {
/* Handle left and find wrongly placed element */
while((array[jl] <= pivotval) && (jl <= jr)) {
if(array[jl] != pivotval) {
if(++il != jl)
swapit(&array[il], &array[jl]);
}
++jl;
}
/* Handle right and find wrongly placed element */
while((array[jr] >= pivotval) && (jl <= jr)) {
if(array[jr] != pivotval) {
if(--ir != jr)
swapit(&array[ir], &array[jr]);
}
--jr;
}
/* Swap the two found elements that are wrongly placed */
if(jl < jr) swapit(&array[jl], &array[jr]);
}
/* return the partition points */
pret3 ret;
ret.leftend = il;
ret.rightend = ir;
return ret;
}
/* RANDOMPIV */ /* RANDOMPIV */
/** /**
@ -225,6 +359,41 @@ static inline void quicksort_rand3(uint32_t array[], int low, int high, rpivotst
} }
} }
/** Randomized pivoting in-place recursive quicksort on array for elements in [low, high] indices */
static inline void quicksort_rand3_sp(uint32_t array[], int low, int high, rpivotstate *state) {
if (low < high) {
/* partition threeway by random pivot */
int pi = pick_pivot(state, (high + 1) - low) + low;
pret3 res;
if(high - low > RAND3_SP_VAL) res = partition3_sp(array, low, high, array[pi]);
else res = partition3(array, low, high, array[pi]);
/* recursive call on the left of pivot */
quicksort_rand3_sp(array, low, res.leftend, state);
/* recursive call on the right of pivot */
quicksort_rand3_sp(array, res.rightend, high, state);
}
}
/** Randomized pivoting in-place recursive quicksort on array for elements in [low, high] indices */
static inline void quicksort_rand3_sp2(uint32_t array[], int low, int high, rpivotstate *state) {
if (low < high) {
/* partition threeway by random pivot */
int pi = pick_pivot(state, (high + 1) - low) + low;
pret3 res;
res = partition3_sp2(array, low, high, array[pi]);
/* if(high - low > RAND3_SP_VAL) res = partition3_sp2(array, low, high, array[pi]);
else res = partition3(array, low, high, array[pi]); */
/* recursive call on the left of pivot */
quicksort_rand3_sp2(array, low, res.leftend, state);
/* recursive call on the right of pivot */
quicksort_rand3_sp2(array, res.rightend, high, state);
}
}
/* MINMAX */ /* MINMAX */
/** /**

View File

@ -89,6 +89,59 @@ static inline void zssort_rand3(uint32_t array[], int low, int high, rpivotstate
} }
} }
/** Always at most log(n) space needing randomized quicksort variant */
static inline void zssort_rand3_sp(uint32_t array[], int low, int high, rpivotstate *state) {
while (low < high) {
int pi = pick_pivot(state, (high + 1) - low) + low;
pret3 res;
if(high - low > RAND3_SP_VAL) res = partition3_sp(array, low, high, array[pi]);
else 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_sp(array, low, res.leftend, state);
// (*) Update partitioning loop for remaining part
low = res.rightend;
} else {
// Right smaller: recurse right of pivot
zssort_rand3_sp(array, res.rightend, high, state);
// (*) Update partitioning loop for remaining part
high = res.leftend; /* high inclusive! */
}
}
}
/** Always at most log(n) space needing randomized quicksort variant */
static inline void zssort_rand3_sp2(uint32_t array[], int low, int high, rpivotstate *state) {
while (low < high) {
int pi = pick_pivot(state, (high + 1) - low) + low;
pret3 res;
res = partition3_sp2(array, low, high, array[pi]);
/*if(high - low > RAND3_SP_VAL) res = partition3_sp(array, low, high, array[pi]);
else 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_sp2(array, low, res.leftend, state);
// (*) Update partitioning loop for remaining part
low = res.rightend;
} else {
// Right smaller: recurse right of pivot
zssort_rand3_sp2(array, res.rightend, high, state);
// (*) Update partitioning loop for remaining part
high = res.leftend; /* high inclusive! */
}
}
}
/* ZSSORTC */ /* ZSSORTC */
/** Always at most log(n) space needing randomized quicksort variant - with checking for sameconst-arrays */ /** Always at most log(n) space needing randomized quicksort variant - with checking for sameconst-arrays */