commit 6fd6dbfff336dcf4295fd104e5e7f71ad9e5d58b Author: Richard Thier Date: Sat Apr 5 02:15:39 2025 +0200 First version of various versions of quicksort diff --git a/data.inc b/data.inc new file mode 100644 index 0000000..f93cf8b --- /dev/null +++ b/data.inc @@ -0,0 +1,13 @@ +int data[] = { + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, + 8, 7, 2, 1, 0, 9, 6, +}; diff --git a/makefile b/makefile new file mode 100644 index 0000000..d375496 --- /dev/null +++ b/makefile @@ -0,0 +1,4 @@ +release: + gcc qs.c -O2 -o qs && ./qs +debug: + gcc qs.c -g -o qs && gdb ./qs diff --git a/qs.c b/qs.c new file mode 100644 index 0000000..62895d6 --- /dev/null +++ b/qs.c @@ -0,0 +1,64 @@ +// gcc qs.c -o qs && ./qs +// Basic tests for various quicksorts + +#include +#include "qsort.h" +#include "zssort.h" + +// function to print array elements +void printArray(int array[], int size) { + for (int i = 0; i < size; ++i) { + printf("%d ", array[i]); + } + printf("\n"); +} + +// qs test function +void qs() { + #include "data.inc" + + int n = sizeof(data) / sizeof(data[0]); + + printf("(qs) Unsorted Array\n"); + printArray(data, n); + + // perform quicksort on data + quicksort(data, 0, n - 1); + + printf("(qs) Sorted array in ascending order: \n"); + printArray(data, n); +} + +// qsr test function +void qsr() { + #include "data.inc" + + int n = sizeof(data) / sizeof(data[0]); + + // perform quicksort on data + rpivotstate state; + quicksort_rand(data, 0, n - 1, &state); + + printf("(qsr) Sorted array in ascending order: \n"); + printArray(data, n); +} + +// qs test function +void zss() { + #include "data.inc" + + int n = sizeof(data) / sizeof(data[0]); + + // perform zssort on data + zssort(data, 0, n - 1); + + printf("(zss) Sorted array in ascending order: \n"); + printArray(data, n); +} + +int main() { + qs(); + qsr(); + zss(); + return 0; +} diff --git a/qsort.h b/qsort.h new file mode 100644 index 0000000..334e5e9 --- /dev/null +++ b/qsort.h @@ -0,0 +1,132 @@ +/* Quick sort in C */ +#ifndef MY_QUICKSORT_H +#define MY_QUICKSORT_H + +#include + +/* Structure: + * + * - BASICS + * - EXTRAS + */ + +/* BASICS */ + +/** Swap operation */ +static inline void swapit(uint32_t *a, uint32_t *b) { + uint32_t t = *a; + *a = *b; + *b = t; +} + +/** + * Partition the array 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!) + */ +static inline int partition(uint32_t array[], int low, int high) { + /* This is "Lomuto"s unidirectional partitioner - see algorithms book */ + + /* select the rightmost element as pivot */ + uint32_t pivot = array[high]; + + /* 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) { + 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); +} + +/** Simple in-place recursive quicksort on array for elements in [low, high) indices */ +static inline void quicksort(uint32_t array[], int low, int high) { + if (low < high) { + int pi = partition(array, low, high); + + /* recursive call on the left of pivot */ + quicksort(array, low, pi - 1); + + /* recursive call on the right of pivot */ + quicksort(array, pi + 1, high); + } +} + +/* EXTRAS */ + +/** + * Partition the array 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!) + */ +static inline int partition_with_pivot(uint32_t array[], int pi, int low, int high) { + /* + * 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(array, low, high); +} + +// 32-bit LCG for fast random generations +static inline uint32_t lcg(uint32_t *state) { + *state = *state * 1664525u + 1013904223u; + return *state; +} + +/** Get pivot index in [0, len-1] without modulus - see our fastrand.h */ +static inline uint32_t pick_pivot(uint32_t *state, uint32_t len) { + uint32_t rand = lcg(state); + /* Multiply by len, take the upper 32 bits of the 64-bit result */ + return (uint32_t)(((uint64_t)rand * len) >> 32); +} + +typedef uint32_t rpivotstate; + +/** Randomized pivoting in-place recursive quicksort on array for elements in [low, high] indices */ +static inline void quicksort_rand(uint32_t array[], int low, int high, rpivotstate *state) { + if (low < high) { + int pi = pick_pivot(state, (high + 1) - low) + low; + pi = partition_with_pivot(array, pi, low, high); + + /* recursive call on the left of pivot */ + quicksort_rand(array, low, pi - 1, state); + + /* recursive call on the right of pivot */ + quicksort_rand(array, pi + 1, high, state); + } +} + +#endif /* MY_QUICKSORT_H */ diff --git a/zssort.h b/zssort.h new file mode 100644 index 0000000..b4c4989 --- /dev/null +++ b/zssort.h @@ -0,0 +1,53 @@ +#ifndef ZS_SORT_H +#define ZS_SORT_H + +#include +#include "qsort.h" + +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! */ + } + } +} + +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! */ + } + } +} + +#endif /* ZS_SORT_H */