I'm not C expert and I've read through the forum, but I still need some advice regarding a sorting problem on C.
I have 4 dynamic arrays of doubles in C. All of them are the same size, and lets say n. What I want to do is to sort all of them using one of the arrays as first order and a second array as my second order. So if the arrays are *x, *y, *w and *z. I want to sort them according to the values of *x, then *y.
I must do this efficiently because the arrays are quite large.
Any help will be much appreciated.
The easy way to do this would be to map your four separate arrays onto a single array of a struct type like
struct rec {
double x;
double y;
double w;
double z;
};
struct rec *arr = malloc( sizeof *arr * N ); // where N is the number of
// elements in each array
if ( !arr )
// malloc failed, handle error somehow
for ( size_t i = 0; i < N; i++ )
{
arr[i].x = x[i];
arr[i].y = y[i];
arr[i].w = w[i];
arr[i].z = z[i];
}
and then create a comparison function to pass to qsort:
int cmpRec( const void *lhs, const void *rhs )
{
struct rec *l = lhs;
struct rec *r = rhs;
if ( l->x < r->x )
return -1;
else if ( l->x > r->x )
return 1;
else
{
if ( l->y < r->y )
return -1;
else if ( l->y > r->y )
return 1;
else
return 0;
}
return 0;
}
Now you can use the qsort library function to sort that array of struct:
qsort( arr, N, sizeof *arr, cmpRec );
Once that array is sorted, you can map the results back onto your four original arrays.
Clearly, sorting this using standard qsort() is not going to work; there isn't a mechanism for passing four arrays.
Equally clearly, if the data were structured as an array of structures, then using qsort() would be feasible.
Question 1: Is it feasible to create an array of structures, load it, sort it, and then unload back into the original arrays?
Question 2: Another option is to sort an array of integers:
int indexes[n];
for (int i = 0; i < n; i++)
indexes[i] = i;
qsort(indexes, n, sizeof(indexes[0]), comparator);
The comparator function would have to be able to access the x and y arrays as file scope variables:
int comparator(void const *v1, void const *v2)
{
int i1 = *(int *)v1;
int i2 = *(int *)v2;
extern double *x, *y;
if (x[i1] > x[i2])
return +1;
else if (x[i1] < x[i2])
return -1;
else if (y[i1] > y[i2])
return +1;
else if (y[i1] < y[i2])
return -1;
else
return 0;
}
You'd then be able to access the arrays using x[indexes[i]] etc to access the ith element in sorted order.
Is that acceptable?
If that is not convenient either, then you will end up writing your own sort; it isn't horribly painful, but will require some care.
I spent some time adapting an existing sort test framework to this scenario. The full code is quite large because it includes a lot of testing support code. The core function (compare, swap, partition and quicksort) are here (122 lines, including comment and blank lines):
/* SO 20271977 - sort arrays x, y, z, w (type double, size n) in parallel based on values in x and y */
/*
** To apply this to the real code, where there are 4 arrays to be sorted
** in parallel, you might write:
**
** Array4 a;
** a.x = x;
** a.y = y;
** a.z = z;
** a.w = w;
** a.n = n;
** quicksort_random(&a);
**
** Or even:
**
** quicksort_random((Array4){ .n = n, .x = x, .y = y, .z = z, .w = w });
**
** combining designated initializers and compound literals. Or you could write a
** trivial wrapper so that you can call:
**
** quicksort_random_wrapper(n, x, y, z, w);
*/
/* SOF so-20271977.h */
#include <stddef.h>
typedef struct Array4
{
size_t n;
double *x;
double *y;
double *z;
double *w;
} Array4;
extern void quicksort_random(Array4 *A);
/* EOF so-20271977.h */
#include <assert.h>
#include <stdlib.h> /* lrand48() */
/*
** Note that a more careful implementation would use nrand48() instead
** of lrand48() to prevent its random number generation from interfering
** with other uses of the x-rand48() functions.
*/
typedef size_t (*Part)(Array4 *A, size_t p, size_t r);
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition);
static size_t partition_random(Array4 *A, size_t p, size_t r);
/* Quick Sort Wrapper function - specifying random partitioning */
void quicksort_random(Array4 *A)
{
quicksort_partition(A, 0, A->n - 1, partition_random);
}
/* Main Quick Sort function */
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition)
{
if (p < r)
{
size_t q = (*partition)(A, p, r);
assert(p <= q && q <= r);
if (q > 0)
quicksort_partition(A, p, q-1, partition);
quicksort_partition(A, q+1, r, partition);
}
}
static inline int compare(Array4 const *A, size_t p, size_t r)
{
if (A->x[p] < A->x[r])
return -1;
else if (A->x[p] > A->x[r])
return +1;
if (A->y[p] < A->y[r])
return -1;
else if (A->y[p] > A->y[r])
return +1;
else
return 0;
}
static inline size_t random_int(size_t p, size_t r)
{
return(lrand48() % (r - p + 1) + p);
}
static inline void swap(Array4 *A, size_t i, size_t j)
{
double d;
d = A->x[i];
A->x[i] = A->x[j];
A->x[j] = d;
d = A->y[i];
A->y[i] = A->y[j];
A->y[j] = d;
d = A->z[i];
A->z[i] = A->z[j];
A->z[j] = d;
d = A->w[i];
A->w[i] = A->w[j];
A->w[j] = d;
}
static size_t partition_random(Array4 *A, size_t p, size_t r)
{
size_t pivot = random_int(p, r);
swap(A, pivot, r);
size_t i = p-1;
size_t j = p;
while (j <= r)
{
if (compare(A, j, r) <= 0)
swap(A, j, ++i);
j++;
}
return i;
}
The test framework (quite ridiculously elaborate if it weren't that I already had a variant of it on hand) is 369 lines including blank lines and comment lines — and all the code above:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define FLTFMT "%13.6f"
typedef struct Array4
{
size_t n;
double *x;
double *y;
double *z;
double *w;
} Array4;
static int trace = 0;
static void *xmalloc(size_t size)
{
void *space = malloc(size);
if (space == 0)
{
fprintf(stderr, "Out of memory (%zu)\n", size);
exit(1);
}
return space;
}
void quicksort_last(Array4 *A);
void quicksort_random(Array4 *A);
void selectionsort(Array4 *A);
static inline int compare(Array4 const *A, size_t p, size_t r)
{
if (A->x[p] < A->x[r])
return -1;
else if (A->x[p] > A->x[r])
return +1;
if (A->y[p] < A->y[r])
return -1;
else if (A->y[p] > A->y[r])
return +1;
else
return 0;
}
static void dump_array(char const *tag, Array4 const *A)
{
printf("%s [%zu..%zu]:\n", tag, (size_t)0, A->n-1);
for (size_t i = 0; i < A->n; i++)
printf("(" FLTFMT ", " FLTFMT ", " FLTFMT ", " FLTFMT ")\n",
A->x[i], A->y[i], A->z[i], A->w[i]);
}
static void chk_sort(Array4 const *A)
{
for (size_t i = 0; i < A->n - 1; i++)
{
//if (compare(A, i, i+1) > 0)
{
if (A->x[i] > A->x[i+1])
{
printf("Out of order: A.x[%zu] = " FLTFMT ", A.x[%zu] = " FLTFMT "\n",
i, A->x[i], i+1, A->x[i+1]);
}
else if ((A->x[i] == A->x[i+1] && A->y[i] > A->y[i+1]))
{
printf("Out of order: A.x[%zu] = " FLTFMT ", A.x[%zu] = " FLTFMT ", "
"A.y[%zu] = " FLTFMT ", A.y[%zu] = " FLTFMT "\n",
i, A->x[i], i+1, A->x[i+1], i, A->y[i], i+1, A->y[i+1]);
}
}
}
}
static inline void set(Array4 *A, size_t p, double d)
{
A->x[p] = d;
A->y[p] = d + drand48() - 0.5;
A->z[p] = d / 2.0;
A->w[p] = d * 2.0;
}
static void load_random(Array4 *A)
{
size_t size = A->n;
for (size_t i = 0; i < size; i++)
{
A->x[i] = drand48() * size;
A->y[i] = drand48() * size + drand48() - 0.5;
A->z[i] = drand48() * size / 2.0;
A->w[i] = drand48() * size * 2.0;
}
}
static void load_ascending(Array4 *A)
{
for (size_t i = 0; i < A->n; i++)
set(A, i, i);
}
static void load_descending(Array4 *A)
{
for (size_t i = 0; i < A->n; i++)
set(A, i, A->n - i);
}
static void load_uniform(Array4 *A)
{
for (size_t i = 0; i < A->n; i++)
set(A, i, A->n);
}
static void load_organpipe(Array4 *A)
{
for (size_t i = 0; i <= A->n / 2; i++)
set(A, i, i);
for (size_t i = A->n / 2 + 1; i < A->n; i++)
set(A, i, A->n - i);
}
static void load_invorganpipe(Array4 *A)
{
size_t range = A->n / 2;
for (size_t i = 0; i < A->n / 2; i++)
set(A, i, range - i);
for (size_t i = A->n / 2 + 1; i < A->n; i++)
set(A, i, i - range);
}
typedef void (*Load)(Array4 *A);
typedef void (*Sort)(Array4 *A);
typedef size_t (*Part)(Array4 *A, size_t p, size_t r);
static void test_one_sort(Array4 *A, Sort sort, char const *s_tag,
char const *l_tag, char const *z_tag)
{
if (trace)
{
printf("%s-%s-%s:", z_tag, l_tag, s_tag);
dump_array("Before", A);
}
clock_t start = clock();
(*sort)(A);
clock_t finish = clock();
double sec = (finish - start) / (double)CLOCKS_PER_SEC;
printf("%s-%s-%s: %13.6f\n", z_tag, l_tag, s_tag, sec);
chk_sort(A);
if (trace)
{
printf("%s-%s-%s:", z_tag, l_tag, s_tag);
dump_array("After", A);
}
fflush(stdout);
}
static Array4 *alloc_array(size_t size)
{
Array4 *A = xmalloc(sizeof(*A));
A->n = size;
A->x = xmalloc(size * sizeof(A->x[0]));
A->y = xmalloc(size * sizeof(A->y[0]));
A->z = xmalloc(size * sizeof(A->z[0]));
A->w = xmalloc(size * sizeof(A->w[0]));
return A;
}
static Array4 *dup_array(Array4 *A)
{
size_t size = A->n;
Array4 *B = alloc_array(size);
if (B != 0)
{
B->n = size;
memmove(B->x, A->x, size * sizeof(A->x[0]));
memmove(B->y, A->y, size * sizeof(A->y[0]));
memmove(B->z, A->z, size * sizeof(A->z[0]));
memmove(B->w, A->w, size * sizeof(A->w[0]));
}
return B;
}
static void free_array(Array4 *A)
{
free(A->x);
free(A->y);
free(A->z);
free(A->w);
free(A);
}
static void test_set_sorts(Array4 *A, char const *l_tag, char const *z_tag)
{
struct sorter
{
Sort function;
char const *tag;
} sort[] =
{
{ quicksort_last, "QS.L" },
{ quicksort_random, "QS.R" },
{ selectionsort, "SS.N" },
};
enum { NUM_SORTS = sizeof(sort) / sizeof(sort[0]) };
for (int i = 0; i < NUM_SORTS; i++)
{
Array4 *B = dup_array(A);
test_one_sort(B, sort[i].function, sort[i].tag, l_tag, z_tag);
free(B);
}
}
static void test_set_loads(size_t size, char const *z_tag)
{
struct loader
{
Load function;
char const *tag;
} load[] =
{
{ load_random, "R" },
{ load_ascending, "A" },
{ load_descending, "D" },
{ load_organpipe, "O" },
{ load_invorganpipe, "I" },
{ load_uniform, "U" },
};
enum { NUM_LOADS = sizeof(load) / sizeof(load[0]) };
Array4 *A = alloc_array(size);
for (int i = 0; i < NUM_LOADS; i++)
{
load[i].function(A);
test_set_sorts(A, load[i].tag, z_tag);
}
free_array(A);
}
/* Main Quick Sort function */
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition)
{
if (p < r)
{
size_t q = (*partition)(A, p, r);
assert(p <= q && q <= r);
if (q > 0)
quicksort_partition(A, p, q-1, partition);
quicksort_partition(A, q+1, r, partition);
}
}
static size_t partition_random(Array4 *A, size_t p, size_t r);
static size_t partition_last(Array4 *A, size_t p, size_t r);
/* Quick Sort Wrapper function - specifying random partitioning */
void quicksort_random(Array4 *A)
{
quicksort_partition(A, 0, A->n - 1, partition_random);
}
/* Quick Sort Wrapper function - specifying partitioning about last element */
void quicksort_last(Array4 *A)
{
quicksort_partition(A, 0, A->n - 1, partition_last);
}
static inline size_t random_int(size_t p, size_t r)
{
return(lrand48() % (r - p + 1) + p);
}
static inline void swap(Array4 *A, size_t i, size_t j)
{
double d;
d = A->x[i];
A->x[i] = A->x[j];
A->x[j] = d;
d = A->y[i];
A->y[i] = A->y[j];
A->y[j] = d;
d = A->z[i];
A->z[i] = A->z[j];
A->z[j] = d;
d = A->w[i];
A->w[i] = A->w[j];
A->w[j] = d;
}
static size_t partition_random(Array4 *A, size_t p, size_t r)
{
size_t pivot = random_int(p, r);
swap(A, pivot, r);
size_t i = p-1;
size_t j = p;
while (j <= r)
{
if (compare(A, j, r) <= 0)
swap(A, j, ++i);
j++;
}
return i;
}
static size_t partition_last(Array4 *A, size_t p, size_t r)
{
size_t i = p-1;
size_t j = p;
while (j <= r)
{
if (compare(A, j, r) <= 0)
swap(A, j, ++i);
j++;
}
return i;
}
/* Selection Sort algorithm */
void selectionsort(Array4 *A)
{
size_t r = A->n;
for (size_t p = 0; p < r; p++)
{
for (size_t i = p; i < r; i++)
{
if (compare(A, p, i) > 0)
swap(A, p, i);
}
}
}
/*
** To apply this to the real code, where there are 4 arrays to be sorted
** in parallel, you might write:
**
** Array4 a;
** a.x = x;
** a.y = y;
** a.z = z;
** a.w = w;
** a.n = n;
** quicksort_random(&a);
**
** Or even:
**
** quicksort_random((Array4){ .n = n, .x = x, .y = y, .z = z, .w = w });
**
** combining designated initializers and compound literals. Or you could write a
** trivial wrapper so that you can call:
**
** quicksort_random_wrapper(n, x, y, z, w);
*/
int main(void)
{
srand48((long)time(0));
for (size_t i = 10; i <= 40; i += 10)
{
char buffer[10];
snprintf(buffer, sizeof(buffer), "%zuK", i);
test_set_loads(1000*i, buffer);
}
return 0;
}
If you can't use qsort with
typedef struct Point {
double x;
double y;
double w;
double z;
} Point;
Use qsort with
typedef struct UglyThing {
double x;
int i;
} UglyThing;
Create an array of size n, fill x with x values, i with index.
Call qsort. At the end, i will store the permutation order.
Swap the three other arrays according to the permutation order.
Then do the same with little arrays ("with same x") in the y direction.
If this ugly trick is not possible, then I don't see any other solution than reinventing the wheel.
(edit : I have just seen Andrew said something very close to this answer...sorry!)
Bye,
Francis
Related
I was trying to implement merge sort in C.
But when I test the code I encounter this error c0000374 in my merge sort function when I try to split array into left right array.
The code is as follows.
typedef struct EntryStruct {
int data;
char *name;
} Entry;
typedef char *String;
void merge(Entry *output, Entry *L, int nL, Entry *R, int nR) {
int i = 0;
int j = 0;
int k = 0;
while (k < nL + nR) {
if ((L[i].data != NULL && L[i].data < R[i].data) || R[j].data == NULL) {
output[k] = L[i];
i++;
} else {
output[k] = R[j];
j++;
}
k++;
}
}
void merge_sort(Entry *entries, int n) {
if (n > 1) {
int mid = n / 2;
Entry *temp = (Entry *)malloc(n * sizeof(Entry));
Entry *left = (Entry *)malloc(mid * sizeof(Entry));
Entry *right = (Entry *)malloc((n - mid) * sizeof(Entry));
for (int l = 0; l < mid; l++)
left[l] = entries[l];
for (int r = mid; r < n; r++)
right[r] = entries[r];
merge_sort(left, mid);
merge_sort(right, n - mid);
merge(temp, left, mid, right, n - mid);
for (int i = 0 ; i < n; i++) {
entries[i] = temp[i];
}
free(temp);
}
}
Entry Entry_create(int data, String name) {
Entry node;
node.name = (String)malloc(strlen(name) + 1);
strcpy(node.name, name);
node.data = data;
return node;
}
void printArrByName(Entry *arr, int s) {
for (int i = 0; i < s; i++) {
printf("%s\n", arr[i].name);
}
}
int main(void) {
Entry *arr = malloc(5 * sizeof(*arr));
arr[0] = Entry_create(5, "abc");
arr[1] = Entry_create(6, "def");
arr[2] = Entry_create(2, "ghijk");
arr[3] = Entry_create(3, "ksdljf");
arr[4] = Entry_create(1, "lsdfjl");
merge_sort(arr, 5);
printArrByName(arr, 5);
free(arr);
}
I want to ask what is the cause of this problem in my case and how to solve it.
Is this happen because I split array in to left right in the wrong way or is it something to do with the initialization of the array.
There are multiple problems in the code causing undefined behavior:
[major: undefined behavior] In the merge_sort function, the loop for (int r = mid; r < n; r++) right[r] = entries[r]; accesses the array pointed to by right beyond the end. You should write:
for (int r = mid; r < n; r++)
right[r - mid] = entries[r];
This bug is a good candidate to explain the observed behavior as it corrupts the malloc() internal data, causing a subsequent call to malloc() to crash.
[major: memory leak] You do not free left, nor right. As a matter of fact, allocating copies of the left and right parts of the array is not even necessary.
[major: undefined behavior] In the merge function, you do not test if i is less than nL, nor of j is less than nR before accessing L[i] or R[j]. Testing if the data member is not NULL does not suffice, accessing an element beyond the end of an array has undefined behavior.
[minor: unstable sort] L[i].data < R[i].data might not preserve the order of entries that have the same data value. You should use L[i].data <= R[i].data to implement stable sorting.
[hint] Defining typedef char *String; is a bad idea. Do not hide pointers behind typedefs, it is confusing and error prone.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct EntryStruct {
int data;
char *name;
} Entry;
#ifdef _MSC_VER
// define strdup on legacy systems
char *strdup(const char *s) {
size_t len = strlen(s);
char *p = (char *)malloc(len + 1);
if (p)
memcpy(p, s, len + 1);
return p;
}
#endif
void merge(Entry *output, Entry *L, int nL, Entry *R, int nR) {
int i = 0;
int j = 0;
int k = 0;
while (k < nL + nR) {
if (i < nL && (j >= nR || L[i].data <= R[j].data)) {
output[k] = L[i];
i++;
} else {
output[k] = R[j];
j++;
}
k++;
}
}
void merge_sort(Entry *entries, int n) {
if (n > 1) {
int mid = n / 2;
Entry *temp;
Entry *left = entries;
Entry *right = entries + mid;
merge_sort(left, mid);
merge_sort(right, n - mid);
temp = (Entry *)malloc(n * sizeof(Entry));
merge(temp, left, mid, right, n - mid);
for (int i = 0; i < n; i++) {
entries[i] = temp[i];
}
free(temp);
}
}
Entry Entry_create(int data, const char *name) {
Entry node;
node.name = strdup(name);
node.data = data;
return node;
}
void printArrByName(Entry *arr, int n) {
for (int i = 0; i < n; i++) {
printf("%s\n", arr[i].name);
}
}
int main(void) {
Entry *arr = malloc(5 * sizeof(*arr));
arr[0] = Entry_create(5, "abc");
arr[1] = Entry_create(6, "def");
arr[2] = Entry_create(2, "ghijk");
arr[3] = Entry_create(3, "ksdljf");
arr[4] = Entry_create(1, "lsdfjl");
merge_sort(arr, 5);
printArrByName(arr, 5);
for (int i = 0; i < 5; i++)
free(arr[i].name);
free(arr);
return 0;
}
Although not needed for small arrays, and since there are answers based on the questions code, here is a somewhat optimized top down merge sort that avoids copy backs by using a pair of mutually recursive functions (...a2a, ...a2b). An entry function does a one time allocation of the temporary array. On my system, it takes less than .5 second to sort an array of 4 million structures.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct EntryStruct {
int data;
char *name;
} Entry;
/* prototypes for mutually recursive functions */
void merge_sort_a2a(Entry *a, Entry *b, int ll, int ee);
void merge_sort_a2b(Entry *a, Entry *b, int ll, int ee);
void merge(Entry *a, Entry *b, int ll, int rr, int ee)
{
int o = ll; /* b[] index */
int l = ll; /* a[] left index */
int r = rr; /* a[] right index */
while(1){
if(a[l].data <= a[r].data){ /* if a[l] <= a[r] */
b[o++] = a[l++]; /* copy a[l] */
if(l < rr) /* if not end of left run */
continue; /* continue (back to while) */
while(r < ee) /* else copy rest of right run */
b[o++] = a[r++];
break; /* and return */
} else { /* else a[l] > a[r] */
b[o++] = a[r++]; /* copy a[r] */
if(r < ee) /* if not end of right run */
continue; /* continue (back to while) */
while(l < rr) /* else copy rest of left run */
b[o++] = a[l++];
break; /* and return */
}
}
}
void merge_sort_a2a(Entry *a, Entry *b, int ll, int ee)
{
int rr;
if(ee - ll < 2){ /* if 1 element */
return; /* return */
}
rr = ll + (ee-ll)/2; /* mid point, start of right run */
merge_sort_a2b(a, b, ll, rr);
merge_sort_a2b(a, b, rr, ee);
merge(b, a, ll, rr, ee);
}
void merge_sort_a2b(Entry *a, Entry *b, int ll, int ee)
{
int rr;
if(ee - ll < 2){ /* if 1 element */
b[ll] = a[ll]; /* copy to b */
return;
}
rr = ll + (ee-ll)/2; /* mid point, start of right run */
merge_sort_a2a(a, b, ll, rr);
merge_sort_a2a(a, b, rr, ee);
merge(a, b, ll, rr, ee);
}
void merge_sort(Entry *a, int n) {
if(n < 2)
return;
Entry *b = malloc(n * sizeof(Entry));
merge_sort_a2a(a, b, 0, n);
free(b);
}
Entry Entry_create(int data, const char *name) {
Entry node;
node.name = _strdup(name); /* _strdup is ISO name */
node.data = data;
return node;
}
void printArrByName(Entry *arr, int n) {
for (int i = 0; i < n; i++) {
printf("%s\n", arr[i].name);
}
}
int main(void) {
Entry *arr = malloc(5 * sizeof(*arr));
arr[0] = Entry_create(5, "abc");
arr[1] = Entry_create(6, "def");
arr[2] = Entry_create(2, "ghijk");
arr[3] = Entry_create(3, "ksdljf");
arr[4] = Entry_create(1, "lsdfjl");
merge_sort(arr, 5);
printArrByName(arr, 5);
for (int i = 0; i < 5; i++)
free(arr[i].name);
free(arr);
return 0;
}
I try to implement some of the algorithms pure generic using C. I stick with the 3-way quicksort but somehow the implementation does not give correct output. The output nearly sorted but some keys aren't where it should be. The code is below. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static void swap(void *x, void *y, size_t size) {
void *tmp = malloc(size);
memcpy(tmp, x, size);
memcpy(x, y, size);
memcpy(y, tmp, size);
free(tmp);
}
static int cmpDouble(const void *i, const void *j) {
if (*(double *)i < *(double *)j)
return 1;
else if (*(double *)i == *(double *)j)
return 0;
else
return -1;
}
void qsort3way(void *base, int lo, int hi, size_t size,
int (*cmp)(const void *, const void *)) {
if (hi <= lo)
return;
else {
char *ptr = (char*)base;
char *v = ptr + lo * size;
int lt = lo, gt = hi;
int i = lo;
while (i <= gt) {
int c = cmp(v, ptr + i * size);
if (c < 0)
swap(ptr + (lt++) * size, ptr + (i++) * size, size);
else if (c > 0)
swap(ptr + i * size, ptr + (gt--) * size, size);
else
i++;
}
qsort3way(base, lo, lt - 1, size, cmp);
qsort3way(base, gt + 1, hi, size, cmp);
}
}
int main(void) {
int i;
double *d = (double*)malloc(sizeof(double) * 100);
for (i = 0; i < 100; i++)
d[i] = (double)rand();
qsort3way(d, 0, 100 -1, sizeof(double), cmpDouble);
for (i = 0; i < 100; i++)
printf("%.10lf\n", d[i]);
free(d);
return 0;
}
sample output:
41.0000000000
153.0000000000
288.0000000000
2082.0000000000
292.0000000000
1869.0000000000
491.0000000000
778.0000000000
1842.0000000000
6334.0000000000
2995.0000000000
8723.0000000000
3035.0000000000
3548.0000000000
4827.0000000000
3902.0000000000
4664.0000000000
5436.0000000000
4966.0000000000
5537.0000000000
5447.0000000000
7376.0000000000
5705.0000000000
6729.0000000000
6868.0000000000
7711.0000000000
9961.0000000000
8942.0000000000
9894.0000000000
9040.0000000000
9741.0000000000
After reading the book link that you provide to #JohnBollinger. I understand how your algorithm work. Your problem is that your pivot move, but you don't change the value of v. Your pivot is at the index lt
char *ptr = base;
int lt = lo, gt = hi; // lt is the pivot
int i = lo + 1; // we don't compare pivot with itself
while (i <= gt) {
int c = cmp(ptr + lt * size, ptr + i * size);
if (c < 0) {
swap(ptr + lt++ * size, ptr + i++ * size, size);
}
else if (c > 0)
swap(ptr + i * size, ptr + gt-- * size, size);
else
i++;
}
qsort3way(base, lo, lt - 1, size, cmp);
qsort3way(base, gt + 1, hi, size, cmp);
I propose you a "proper" solution:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef void qsort3way_swap(void *a, void *b);
typedef int qsort3way_cmp(void const *a, void const *b);
static void qsort3way_aux(char *array_begin, char *array_end, size_t size,
qsort3way_cmp *cmp, qsort3way_swap *swap) {
if (array_begin < array_end) {
char *i = array_begin + size;
char *lower = array_begin;
char *greater = array_end;
while (i < greater) {
int ret = cmp(lower, i);
if (ret < 0) {
swap(i, lower);
i += size;
lower += size;
} else if (ret > 0) {
greater -= size;
swap(i, greater);
} else {
i += size;
}
}
qsort3way_aux(array_begin, lower, size, cmp, swap);
qsort3way_aux(greater, array_end, size, cmp, swap);
}
}
static void qsort3way(void *array_begin, void *array_end, size_t size,
qsort3way_cmp *cmp, qsort3way_swap *swap) {
qsort3way_aux(array_begin, array_end, size, cmp, swap);
}
static void swap_int_aux(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
static void swap_int(void *a, void *b) { swap_int_aux(a, b); }
static int cmp_int_aux(int const *a, int const *b) {
if (*a < *b) {
return 1;
} else if (*a > *b) {
return -1;
} else {
return 0;
}
}
static int cmp_int(void const *a, void const *b) { return cmp_int_aux(a, b); }
static void print_int(char const *intro, int const *array, size_t const size) {
printf("%s:", intro);
for (size_t i = 0; i < size; i++) {
printf(" %d", array[i]);
}
printf("\n");
}
#define SIZE 42
int main(void) {
int array[SIZE];
srand((unsigned int)time(NULL));
for (size_t i = 0; i < SIZE; i++) {
array[i] = rand() % SIZE - SIZE / 2;
}
print_int("before", array, SIZE);
qsort3way(array, array + SIZE, sizeof *array, cmp_int, swap_int);
print_int("after", array, SIZE);
}
Note: The optimization int i = lo + 1; and char *i = array_begin + size; are mandatory. Because in the case where the function compare return that pivot != pivot this will lead to a infinite recursion. How this would be possible?
The function cmp is bug.
double has strange power... A double can be not equal to itself! (-nan).
The implementation does not give the correct result because it is wrong. Pretty badly wrong, in fact, given that it's supposed to be a three-way quicksort and not a regular one.
One basic problem is that you've omitted the bit where you move the pivot(s) into their proper position after the main partitioning loop. For standard quicksort, that requires one extra swap or assignment after the loop, depending on implementation details. For a three-way quicksort that involves one or two extra loops to move the potentially-many values equal to the pivot into their positions.
A more insidious problem is the one #Stargateur first pointed out: you track the pivot element by pointer, not by value, and you (sometimes) swap the original value out from that position in the course of the partitioning loop.
Furthermore, your main partitioning loop is wrong for a three-way quicksort, too. When you encounter an element equal to the pivot you just leave it in place, but you need instead to move it to one end or the other (or to some kind of auxiliary storage, if you're willing to incur that memory cost) so that you can perform that move to the middle at the end. In a sense, the previous problem is a special case of this one -- you're not reserving space for or tracking the pivot values. Fixing this will solve the previous problem as well.
I'm not sure what reference you used to prepare your implementation, or whether you built it from scratch, but Geeks for Geeks has a C++ (but pretty much also C) implementation for int arrays that you might want to check out.
Your implementation is incorrect because the pivot may move during the partitioning phase and you use a pointer for the comparison which no longer points to it. Implementations in other languages use the value of the pivot instead of its address.
Note also these shortcomings:
recursing both ways may cause stack overflow on pathological distributions. In you case, an array that is already sorted is a pathological distribution.
the comparison function should return the opposite values: -1 if a < b, +1 is a > b and 0 if a == b.
the API is non-standard and confusing: you should pass the number of elements instead of a range with included bounds.
Here is a corrected and commented version:
#include <stdio.h>
#include <stdlib.h>
static void swap(unsigned char *x, unsigned char *y, size_t size) {
/* sub-optimal, but better than malloc */
while (size-- > 0) {
unsigned char c = *x;
*x++ = *y;
*y++ = c;
}
}
void qsort3way(void *base, int n, size_t size,
int (*cmp)(const void *, const void *))
{
unsigned char *ptr = (unsigned char *)base;
while (n > 1) {
/* use first element as pivot, pointed to by lt */
int i = 1, lt = 0, gt = n;
while (i < gt) {
int c = cmp(ptr + lt * size, ptr + i * size);
if (c > 0) {
/* move smaller element before the pivot range */
swap(ptr + lt * size, ptr + i * size, size);
lt++;
i++;
} else if (c < 0) {
/* move larger element to the end */
gt--;
swap(ptr + i * size, ptr + gt * size, size);
/* test with that element again */
} else {
/* leave identical element alone */
i++;
}
}
/* array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* recurse on smaller part, loop on larger to minimize
stack use for pathological distributions */
if (lt < n - gt) {
qsort3way(ptr, lt, size, cmp);
ptr += gt * size;
n -= gt;
} else {
qsort3way(ptr + gt * size, n - gt, size, cmp);
n = lt;
}
}
}
static int cmp_double(const void *i, const void *j) {
/* this comparison function does not handle NaNs */
if (*(const double *)i < *(const double *)j)
return -1;
if (*(const double *)i > *(const double *)j)
return +1;
else
return 0;
}
int main(void) {
double d[100];
int i;
for (i = 0; i < 100; i++)
d[i] = rand() / ((double)RAND_MAX + 1);
qsort3way(d, 100, sizeof(*d), cmp_double);
for (i = 0; i < 100; i++)
printf("%.10lf\n", d[i]);
return 0;
}
I'm trying to make a generic quicksort function, and I fail to understand what's wrong with what I'm doing, because it's not working properly.
Here is my code:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
typedef bool (*CmpFunction)(void*, void*);
int cmp(const void *c1, const void *c2)
{
assert(c1 && c2);
int a = *(const int*)c1;
int b = *(const int*)c2;
if (a > b) return 1;
if (a < b) return -1;
return 0;
}
void swap(void *a, void *b, size_t size) {
char tmp[size];
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
}
void quick_sort(void* a, int n, int size, CmpFunction cmp)
{
int b = 1, t = n - 1;
if (n < 2)
return;
swap((char*)a, (char*)a+(n/2)*size, size);
char p = *(char*)a;
while(b <= t) {
while(t >= b && cmp((char*)a + t*size, &p) >= 0)
t--;
while(b <= t && cmp((char*)a + b*size, &p) < 0)
b++;
if (b < t)
swap((char*)a+size*(b++), (char*)a+size*(t--), size);
}
swap((char*)a, (char*)a+t*size, size);
quick_sort(a, t, size, cmp);
n=n-t-1;
quick_sort((char*)a + (t + 1)*size, n, size, cmp);
}
While the original quicksort function, without me trying to make it generic is:
void quick_sort(int a[], int n)
{
int p, b = 1, t = n - 1;
if (n < 2)
return;
swap(&a[0], &a[n/2]);
p = a[0];
while(b <= t) {
while(t >= b && a[t] >= p )
t--;
while(b <= t && a[b] < p)
b++;
if ( b < t)
swap(&a[b++], &a[t--]);
}
swap(&a[0], &a[t]);
quick_sort(a, t);
n=n-t-1;
quick_sort(a + t + 1, n);
}
void swap(int *c1, int *c2)
{
int c = *c1;
*c1 = *c2;
*c2 = c;
}
I'm using this main():
int main(){
char b[] = {'a','t','b','c','y','s'};
int c[] = {1,4,6,3,5,7};
quick_sort(c, 6, sizeof(c[0]), &cmp);
for (int i=0;i<6;i++)
printf("%d | ", c[i]);
return 0;
}
Now we all agree that the output should be:
1, 3, 4, 5, 6, 7
which is indeed what I get when running the NOT generic function.
When I run my generic(upper) function I get this:
5 | 1 | 4 | 7 | 6 | 3 |
You all have any ideas where I'm wrong? :)
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int integerCompare(const void *c1, const void *c2) // use this to compare int
{
int a = *(const int*) c1;
int b = *(const int*) c2;
if (a > b) {
return 1;
} else if(a < b) {
return -1;
} else {
return 0;
}
}
int characterCompare(const void *c1, const void *c2) // use this to compare char
{
char a = *(const int*) c1;
char b = *(const int*) c2;
if (a > b) {
return 1;
} else if(a < b) {
return -1;
} else {
return 0;
}
}
void swap(void *a, void *b, size_t size) {
// instead of using an array, use a void pointer and malloc
void* tmp = malloc(size);
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
free(tmp); // free the allocated space
}
void quick_sort(void* a, int n, int size, int (*cmp)(const void*, const void*))
{
int b = 1;
int t = n - 1;
if (n < 2) {
return;
}
swap(a, a + (n/2) * size, size); // &a[0] == a
// you do not need that p variable .. its value is not being modified
while(b <= t) {
while(t >= b && cmp(a + t*size, a) >= 0)
t--;
while(b <= t && cmp(a + b*size, a) < 0)
b++;
if (b < t)
swap(a + size * (b++), a + size * (t--), size);
}
swap(a, a + t * size, size);
quick_sort(a, t, size, cmp);
n= n - t - 1;
quick_sort(a + (t + 1)*size, n, size, cmp);
}
int main()
{
char b[] = {'a','t','b','c','y','s'};
int c[] = {1,4,6,3,5,7};
quick_sort(c, 6, sizeof(c[0]), &integerCompare);
for (int i=0;i<6;i++) {
printf("%d | ", c[i]);
}
printf("\n");
quick_sort(b, 6, sizeof(b[0]), &characterCompare);
for (int i=0;i<6;i++) {
printf("%c | ", b[i]);
}
return 0;
}
I'm not C expert and I've read through the forum, but I still need some advice regarding a sorting problem on C.
I have 4 dynamic arrays of doubles in C. All of them are the same size, and lets say n. What I want to do is to sort all of them using one of the arrays as first order and a second array as my second order. So if the arrays are *x, *y, *w and *z. I want to sort them according to the values of *x, then *y.
I must do this efficiently because the arrays are quite large.
Any help will be much appreciated.
The easy way to do this would be to map your four separate arrays onto a single array of a struct type like
struct rec {
double x;
double y;
double w;
double z;
};
struct rec *arr = malloc( sizeof *arr * N ); // where N is the number of
// elements in each array
if ( !arr )
// malloc failed, handle error somehow
for ( size_t i = 0; i < N; i++ )
{
arr[i].x = x[i];
arr[i].y = y[i];
arr[i].w = w[i];
arr[i].z = z[i];
}
and then create a comparison function to pass to qsort:
int cmpRec( const void *lhs, const void *rhs )
{
struct rec *l = lhs;
struct rec *r = rhs;
if ( l->x < r->x )
return -1;
else if ( l->x > r->x )
return 1;
else
{
if ( l->y < r->y )
return -1;
else if ( l->y > r->y )
return 1;
else
return 0;
}
return 0;
}
Now you can use the qsort library function to sort that array of struct:
qsort( arr, N, sizeof *arr, cmpRec );
Once that array is sorted, you can map the results back onto your four original arrays.
Clearly, sorting this using standard qsort() is not going to work; there isn't a mechanism for passing four arrays.
Equally clearly, if the data were structured as an array of structures, then using qsort() would be feasible.
Question 1: Is it feasible to create an array of structures, load it, sort it, and then unload back into the original arrays?
Question 2: Another option is to sort an array of integers:
int indexes[n];
for (int i = 0; i < n; i++)
indexes[i] = i;
qsort(indexes, n, sizeof(indexes[0]), comparator);
The comparator function would have to be able to access the x and y arrays as file scope variables:
int comparator(void const *v1, void const *v2)
{
int i1 = *(int *)v1;
int i2 = *(int *)v2;
extern double *x, *y;
if (x[i1] > x[i2])
return +1;
else if (x[i1] < x[i2])
return -1;
else if (y[i1] > y[i2])
return +1;
else if (y[i1] < y[i2])
return -1;
else
return 0;
}
You'd then be able to access the arrays using x[indexes[i]] etc to access the ith element in sorted order.
Is that acceptable?
If that is not convenient either, then you will end up writing your own sort; it isn't horribly painful, but will require some care.
I spent some time adapting an existing sort test framework to this scenario. The full code is quite large because it includes a lot of testing support code. The core function (compare, swap, partition and quicksort) are here (122 lines, including comment and blank lines):
/* SO 20271977 - sort arrays x, y, z, w (type double, size n) in parallel based on values in x and y */
/*
** To apply this to the real code, where there are 4 arrays to be sorted
** in parallel, you might write:
**
** Array4 a;
** a.x = x;
** a.y = y;
** a.z = z;
** a.w = w;
** a.n = n;
** quicksort_random(&a);
**
** Or even:
**
** quicksort_random((Array4){ .n = n, .x = x, .y = y, .z = z, .w = w });
**
** combining designated initializers and compound literals. Or you could write a
** trivial wrapper so that you can call:
**
** quicksort_random_wrapper(n, x, y, z, w);
*/
/* SOF so-20271977.h */
#include <stddef.h>
typedef struct Array4
{
size_t n;
double *x;
double *y;
double *z;
double *w;
} Array4;
extern void quicksort_random(Array4 *A);
/* EOF so-20271977.h */
#include <assert.h>
#include <stdlib.h> /* lrand48() */
/*
** Note that a more careful implementation would use nrand48() instead
** of lrand48() to prevent its random number generation from interfering
** with other uses of the x-rand48() functions.
*/
typedef size_t (*Part)(Array4 *A, size_t p, size_t r);
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition);
static size_t partition_random(Array4 *A, size_t p, size_t r);
/* Quick Sort Wrapper function - specifying random partitioning */
void quicksort_random(Array4 *A)
{
quicksort_partition(A, 0, A->n - 1, partition_random);
}
/* Main Quick Sort function */
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition)
{
if (p < r)
{
size_t q = (*partition)(A, p, r);
assert(p <= q && q <= r);
if (q > 0)
quicksort_partition(A, p, q-1, partition);
quicksort_partition(A, q+1, r, partition);
}
}
static inline int compare(Array4 const *A, size_t p, size_t r)
{
if (A->x[p] < A->x[r])
return -1;
else if (A->x[p] > A->x[r])
return +1;
if (A->y[p] < A->y[r])
return -1;
else if (A->y[p] > A->y[r])
return +1;
else
return 0;
}
static inline size_t random_int(size_t p, size_t r)
{
return(lrand48() % (r - p + 1) + p);
}
static inline void swap(Array4 *A, size_t i, size_t j)
{
double d;
d = A->x[i];
A->x[i] = A->x[j];
A->x[j] = d;
d = A->y[i];
A->y[i] = A->y[j];
A->y[j] = d;
d = A->z[i];
A->z[i] = A->z[j];
A->z[j] = d;
d = A->w[i];
A->w[i] = A->w[j];
A->w[j] = d;
}
static size_t partition_random(Array4 *A, size_t p, size_t r)
{
size_t pivot = random_int(p, r);
swap(A, pivot, r);
size_t i = p-1;
size_t j = p;
while (j <= r)
{
if (compare(A, j, r) <= 0)
swap(A, j, ++i);
j++;
}
return i;
}
The test framework (quite ridiculously elaborate if it weren't that I already had a variant of it on hand) is 369 lines including blank lines and comment lines — and all the code above:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define FLTFMT "%13.6f"
typedef struct Array4
{
size_t n;
double *x;
double *y;
double *z;
double *w;
} Array4;
static int trace = 0;
static void *xmalloc(size_t size)
{
void *space = malloc(size);
if (space == 0)
{
fprintf(stderr, "Out of memory (%zu)\n", size);
exit(1);
}
return space;
}
void quicksort_last(Array4 *A);
void quicksort_random(Array4 *A);
void selectionsort(Array4 *A);
static inline int compare(Array4 const *A, size_t p, size_t r)
{
if (A->x[p] < A->x[r])
return -1;
else if (A->x[p] > A->x[r])
return +1;
if (A->y[p] < A->y[r])
return -1;
else if (A->y[p] > A->y[r])
return +1;
else
return 0;
}
static void dump_array(char const *tag, Array4 const *A)
{
printf("%s [%zu..%zu]:\n", tag, (size_t)0, A->n-1);
for (size_t i = 0; i < A->n; i++)
printf("(" FLTFMT ", " FLTFMT ", " FLTFMT ", " FLTFMT ")\n",
A->x[i], A->y[i], A->z[i], A->w[i]);
}
static void chk_sort(Array4 const *A)
{
for (size_t i = 0; i < A->n - 1; i++)
{
//if (compare(A, i, i+1) > 0)
{
if (A->x[i] > A->x[i+1])
{
printf("Out of order: A.x[%zu] = " FLTFMT ", A.x[%zu] = " FLTFMT "\n",
i, A->x[i], i+1, A->x[i+1]);
}
else if ((A->x[i] == A->x[i+1] && A->y[i] > A->y[i+1]))
{
printf("Out of order: A.x[%zu] = " FLTFMT ", A.x[%zu] = " FLTFMT ", "
"A.y[%zu] = " FLTFMT ", A.y[%zu] = " FLTFMT "\n",
i, A->x[i], i+1, A->x[i+1], i, A->y[i], i+1, A->y[i+1]);
}
}
}
}
static inline void set(Array4 *A, size_t p, double d)
{
A->x[p] = d;
A->y[p] = d + drand48() - 0.5;
A->z[p] = d / 2.0;
A->w[p] = d * 2.0;
}
static void load_random(Array4 *A)
{
size_t size = A->n;
for (size_t i = 0; i < size; i++)
{
A->x[i] = drand48() * size;
A->y[i] = drand48() * size + drand48() - 0.5;
A->z[i] = drand48() * size / 2.0;
A->w[i] = drand48() * size * 2.0;
}
}
static void load_ascending(Array4 *A)
{
for (size_t i = 0; i < A->n; i++)
set(A, i, i);
}
static void load_descending(Array4 *A)
{
for (size_t i = 0; i < A->n; i++)
set(A, i, A->n - i);
}
static void load_uniform(Array4 *A)
{
for (size_t i = 0; i < A->n; i++)
set(A, i, A->n);
}
static void load_organpipe(Array4 *A)
{
for (size_t i = 0; i <= A->n / 2; i++)
set(A, i, i);
for (size_t i = A->n / 2 + 1; i < A->n; i++)
set(A, i, A->n - i);
}
static void load_invorganpipe(Array4 *A)
{
size_t range = A->n / 2;
for (size_t i = 0; i < A->n / 2; i++)
set(A, i, range - i);
for (size_t i = A->n / 2 + 1; i < A->n; i++)
set(A, i, i - range);
}
typedef void (*Load)(Array4 *A);
typedef void (*Sort)(Array4 *A);
typedef size_t (*Part)(Array4 *A, size_t p, size_t r);
static void test_one_sort(Array4 *A, Sort sort, char const *s_tag,
char const *l_tag, char const *z_tag)
{
if (trace)
{
printf("%s-%s-%s:", z_tag, l_tag, s_tag);
dump_array("Before", A);
}
clock_t start = clock();
(*sort)(A);
clock_t finish = clock();
double sec = (finish - start) / (double)CLOCKS_PER_SEC;
printf("%s-%s-%s: %13.6f\n", z_tag, l_tag, s_tag, sec);
chk_sort(A);
if (trace)
{
printf("%s-%s-%s:", z_tag, l_tag, s_tag);
dump_array("After", A);
}
fflush(stdout);
}
static Array4 *alloc_array(size_t size)
{
Array4 *A = xmalloc(sizeof(*A));
A->n = size;
A->x = xmalloc(size * sizeof(A->x[0]));
A->y = xmalloc(size * sizeof(A->y[0]));
A->z = xmalloc(size * sizeof(A->z[0]));
A->w = xmalloc(size * sizeof(A->w[0]));
return A;
}
static Array4 *dup_array(Array4 *A)
{
size_t size = A->n;
Array4 *B = alloc_array(size);
if (B != 0)
{
B->n = size;
memmove(B->x, A->x, size * sizeof(A->x[0]));
memmove(B->y, A->y, size * sizeof(A->y[0]));
memmove(B->z, A->z, size * sizeof(A->z[0]));
memmove(B->w, A->w, size * sizeof(A->w[0]));
}
return B;
}
static void free_array(Array4 *A)
{
free(A->x);
free(A->y);
free(A->z);
free(A->w);
free(A);
}
static void test_set_sorts(Array4 *A, char const *l_tag, char const *z_tag)
{
struct sorter
{
Sort function;
char const *tag;
} sort[] =
{
{ quicksort_last, "QS.L" },
{ quicksort_random, "QS.R" },
{ selectionsort, "SS.N" },
};
enum { NUM_SORTS = sizeof(sort) / sizeof(sort[0]) };
for (int i = 0; i < NUM_SORTS; i++)
{
Array4 *B = dup_array(A);
test_one_sort(B, sort[i].function, sort[i].tag, l_tag, z_tag);
free(B);
}
}
static void test_set_loads(size_t size, char const *z_tag)
{
struct loader
{
Load function;
char const *tag;
} load[] =
{
{ load_random, "R" },
{ load_ascending, "A" },
{ load_descending, "D" },
{ load_organpipe, "O" },
{ load_invorganpipe, "I" },
{ load_uniform, "U" },
};
enum { NUM_LOADS = sizeof(load) / sizeof(load[0]) };
Array4 *A = alloc_array(size);
for (int i = 0; i < NUM_LOADS; i++)
{
load[i].function(A);
test_set_sorts(A, load[i].tag, z_tag);
}
free_array(A);
}
/* Main Quick Sort function */
static void quicksort_partition(Array4 *A, size_t p, size_t r, Part partition)
{
if (p < r)
{
size_t q = (*partition)(A, p, r);
assert(p <= q && q <= r);
if (q > 0)
quicksort_partition(A, p, q-1, partition);
quicksort_partition(A, q+1, r, partition);
}
}
static size_t partition_random(Array4 *A, size_t p, size_t r);
static size_t partition_last(Array4 *A, size_t p, size_t r);
/* Quick Sort Wrapper function - specifying random partitioning */
void quicksort_random(Array4 *A)
{
quicksort_partition(A, 0, A->n - 1, partition_random);
}
/* Quick Sort Wrapper function - specifying partitioning about last element */
void quicksort_last(Array4 *A)
{
quicksort_partition(A, 0, A->n - 1, partition_last);
}
static inline size_t random_int(size_t p, size_t r)
{
return(lrand48() % (r - p + 1) + p);
}
static inline void swap(Array4 *A, size_t i, size_t j)
{
double d;
d = A->x[i];
A->x[i] = A->x[j];
A->x[j] = d;
d = A->y[i];
A->y[i] = A->y[j];
A->y[j] = d;
d = A->z[i];
A->z[i] = A->z[j];
A->z[j] = d;
d = A->w[i];
A->w[i] = A->w[j];
A->w[j] = d;
}
static size_t partition_random(Array4 *A, size_t p, size_t r)
{
size_t pivot = random_int(p, r);
swap(A, pivot, r);
size_t i = p-1;
size_t j = p;
while (j <= r)
{
if (compare(A, j, r) <= 0)
swap(A, j, ++i);
j++;
}
return i;
}
static size_t partition_last(Array4 *A, size_t p, size_t r)
{
size_t i = p-1;
size_t j = p;
while (j <= r)
{
if (compare(A, j, r) <= 0)
swap(A, j, ++i);
j++;
}
return i;
}
/* Selection Sort algorithm */
void selectionsort(Array4 *A)
{
size_t r = A->n;
for (size_t p = 0; p < r; p++)
{
for (size_t i = p; i < r; i++)
{
if (compare(A, p, i) > 0)
swap(A, p, i);
}
}
}
/*
** To apply this to the real code, where there are 4 arrays to be sorted
** in parallel, you might write:
**
** Array4 a;
** a.x = x;
** a.y = y;
** a.z = z;
** a.w = w;
** a.n = n;
** quicksort_random(&a);
**
** Or even:
**
** quicksort_random((Array4){ .n = n, .x = x, .y = y, .z = z, .w = w });
**
** combining designated initializers and compound literals. Or you could write a
** trivial wrapper so that you can call:
**
** quicksort_random_wrapper(n, x, y, z, w);
*/
int main(void)
{
srand48((long)time(0));
for (size_t i = 10; i <= 40; i += 10)
{
char buffer[10];
snprintf(buffer, sizeof(buffer), "%zuK", i);
test_set_loads(1000*i, buffer);
}
return 0;
}
If you can't use qsort with
typedef struct Point {
double x;
double y;
double w;
double z;
} Point;
Use qsort with
typedef struct UglyThing {
double x;
int i;
} UglyThing;
Create an array of size n, fill x with x values, i with index.
Call qsort. At the end, i will store the permutation order.
Swap the three other arrays according to the permutation order.
Then do the same with little arrays ("with same x") in the y direction.
If this ugly trick is not possible, then I don't see any other solution than reinventing the wheel.
(edit : I have just seen Andrew said something very close to this answer...sorry!)
Bye,
Francis
I have a generic quicksort function:
void qsort(void* sup,int n,
int(*cmp) (void *x,void *y),
void (*swap) (void *a,void *b))
{
int pv=n/2,l=0,h=n-1;
if(n<2)
return;
while(h-1>=l)
{
if(l==pv)
{
swap(sup+pv,sup+pv+1);
pv++;
}
if(h==pv)
{
swap(sup+pv,sup+(pv-1));
pv--;
}
if(cmp(sup+h, sup+pv))
{
h--;
continue;
}
if(cmp(sup+pv, sup+l))
{
l++;
continue;
}
swap(sup+l,sup+h);
l++,h--;
}
qsort(sup, l, cmp, swap);
qsort(sup+l,n-l, cmp, swap);
}
with these function as parameter:
int cmp(int *c1, int *c2) {
return *c1 > *c2;
}
void swap(int *a,int *b)
{
int c= *a;
*a=*b;
*b=c;
}
the main function is the following:
int main()
{
int arr[4] = {3,4,1,2};
print(arr, 4);
printf("\n\n");
qsort(arr, 4, &cmp, &swap);
print(arr, 4);
return 0;
}
where print is:
void print(int* arr, int size) {
int i = 0;
for(; i < size; ++i) {
printf("%d \t", arr[i]);
}
}
The problem:
When the prototype of qsort is:
void qsort(int* sup,int n,
int(*cmp) (void *x,void *y),
void (*swap) (void *a,void *b))
it works great,
but when I change the sup parameter to void*:
void qsort(void* sup,int n,
int(*cmp) (void *x,void *y),
void (*swap) (void *a,void *b))
it doesn't work. do anybody have any idea why?
I'm working with Code::Blocks under Windows, with MinGW.
You must be able to dereference your parameters. void* cannot be dereferenced as the compiler cannot determine the type you are passing. You must use explicit casting if you pass void*.
This
void qsort(int* sup,int n,
int(*cmp) (void *x,void *y),
void (*swap) (void *a,void *b))
is absolutely fine, but here
void qsort(void* sup,int n,
int(*cmp) (void *x,void *y),
void (*swap) (void *a,void *b))
you are passing a void* (sup) and this one cannot be dereferenced. So your first solution is fine but you must either typecast in (*cmp) or define a qsort for every type.
Here's how you could fix the sort:
#include <stdio.h>
void Qsort(
void* sup,
int n,
int size,
int(*cmp) (const void *x, const void *y),
void (*swap) (void *a,void *b))
{
int pv = n / 2, l = 0, h = n - 1;
if (n < 2)
return;
while (h - 1 >= l)
{
if (l == pv)
{
swap((char*)sup + pv * size, (char*)sup + (pv + 1) * size);
pv++;
}
if(h == pv)
{
swap((char*)sup + pv * size, (char*)sup + (pv - 1) * size);
pv--;
}
if (cmp((char*)sup + h * size, (char*)sup + pv * size) > 0)
{
h--;
continue;
}
if (cmp((char*)sup + pv * size, (char*)sup + l * size) > 0)
{
l++;
continue;
}
swap((char*)sup + l * size, (char*)sup + h * size);
l++, h--;
}
Qsort(sup, l, size, cmp, swap);
Qsort((char*)sup + l * size, n - l, size, cmp, swap);
}
int cmp(const void *c1, const void *c2)
{
int a = *(const int*)c1;
int b = *(const int*)c2;
if (a > b) return 1;
if (a < b) return -1;
return 0;
}
void swap(void *c1, void *c2)
{
int c = *(int*)c1;
*(int*)c1 = *(int*)c2;
*(int*)c2 = c;
}
void print(int* arr, int size)
{
int i = 0;
for(; i < size; ++i)
{
printf("%d \t", arr[i]);
}
}
int main(void)
{
int arr[4] = {3,4,1,2};
print(arr, 4);
printf("\n\n");
Qsort(arr, 4, sizeof(arr[0]), &cmp, &swap);
print(arr, 4);
return 0;
}
Output (ideone):
3 4 1 2
1 2 3 4
Note that naming your functions the same way as standard library functions is a bad thing to do. Your program might fail to compile or work. For this reason I changed qsort to Qsort.
we can improve a generic quick sort to omit swap function.
#ifdef _WIN32
#define alloca _alloca
#else
#include <alloca.h>
#endif
// the generic swap function
void arrswap(void * const a, void * const b, int const sz) {
int64_t tmp;
void * p;
bool needfree = false;
if (sz > sizeof(int64_t)) {
p = alloca(sz);
if (p == NULL) {
p = malloc(sz);
//assert(p != NULL, "not enough memory");
needfree = true;
}
}
else {
p = &tmp;
}
memcpy(p, b, sz);
memcpy(b, a, sz);
memcpy(a, p, sz);
if (needfree) {
free(p);
}
}
// O(n^2) sort
void arrsort(void * const p, int const sz, int const n,
int(*compare)(void const * const pa, void const *const pb)) {
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (compare((char*)p + i*sz, (char*)p + j*sz) == 1) {
arrswap((char*)p + i*sz, (char*)p + j*sz, sz);
}
}
}
}
// guess the index of the pivot value from three value in the array.
static int guessmidval(void * const p, int const sz, int const n,
int(*compare)(void const * const pa, void const *const pb)) {
int a = 0;
int b = n / 2;
int c = n - 1;
int ab = compare((char*)p + a*sz, (char*)p + b*sz);
int bc = compare((char*)p + b*sz, (char*)p + c*sz);
int cb = compare((char*)p + c*sz, (char*)p + b*sz);
int ba = compare((char*)p + b*sz, (char*)p + a*sz);
if (ab <= 0 && bc <= 0 || cb <= 0 && ba <= 0) {
return b;
}
int ac = compare((char*)p + a*sz, (char*)p + c*sz);
int ca = compare((char*)p + c*sz, (char*)p + a*sz);
if (ba <= 0 && ac <= 0 || ca <= 0 && ab <= 0) {
return a;
}
return c;
}
// quick sort
void arrqsort(void * const p, int sz, int const n,
int(*compare)(void const * const pa, void const *const pb)) {
if (n <= 2) {
arrsort(p, sz, n, compare);
return;
}
int midval_index = guessmidval(p, sz, n, compare);
arrswap(p, (char*)p + midval_index*sz, sz);
int i, j;
for (i = 1, j = n - 1; ; i++, j--) {
for (; compare((char*)p + i*sz, p) <= -1 && i <= j; i++);
for (; compare((char*)p + j*sz, p) >= +1 && i <= j; j--);
if (i >= j)break;
arrswap((char*)p + i*sz, (char*)p + j*sz, sz);
}
arrqsort(p, sz, i, compare);
arrqsort((char*)p + i*sz, sz, n - i, compare);
}
creating a compare function for int64:
int compare_int64(void const * const pa, void const *const pb) {
int64_t a = *(int64_t*)pa;
int64_t b = *(int64_t*)pb;
if (a > b)return 1;
else if (a < b)return -1;
return 0;
}
we can call arrqsort like :
int64_t a[] = { 3,1,65,4,-1 };
int n = sizeof(a) / sizeof(*a);
arrqsort(a, sizeof(*a), n, compare_int64);
for (int j = 0; j < n; j++) {//-1,1,3,4,65,
printf("%d,", a[j]);
}