Overview:
I'm implementing a program that uses the quick sort algorithm to order different arrays which have different sizes and are ordered in different ways. Then I print out how long it took for each array to be ordered and how many exchanges and comparisons have been made.
It's a homework and I need to know all of the different times, exchanges and comparisons.
What I tried
Reading articles online, I found out that it might be my compiler, it might use too few memory and it blocks the compiling process so I have to compile in release mode. The release mode gives me the same problems. I tried online compilers but none of em work.
How my program works
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define CINQUECENTO 500
#define MILLE 1000
#define DUEMILA 2000
#define CINQUEMILA 5000
#define DIECIMILA 10000
#define VENTIMILA 20000
#define CINQUANTAMILA 50000
typedef enum {ORINATO, INVERS, PARZ_ORDINATO, RANDOM} Ordine;
typedef enum{FIVEH, ONET, TWOT, FIVET, TENT, TWENTYT, FIFTYT} Dimensione;
int qconfronti=0, qscambi=0;
int *generaArray(int dimensione, Ordine ordine);
int perno(int* array, int primo, int ultimo);
void quickSort(int* array, int u, int v);
void calculateQuicktime(int *array, int dim);
void automaticQS();
int main(){
automaticQS();
return 0;
}
int *generaArray(int dimensione, Ordine ordine) {
int i, j, n;
int *array = malloc(dimensione * sizeof(int));
if (!array){
return NULL;
}
switch (ordine){
case ORINATO:
for (i = 0; i < dimensione; i++){
array[i] = i;
}
break;
case INVERS:
n =0;
for ( i = dimensione-1; i >= 0 ; i--) {
array[i] = n;
n++;
}break;
case PARZ_ORDINATO:
for (i = 0; i < dimensione/2 ; i++) {
array[i] = i;
}
for (j = i+1; j <dimensione; j++){
n = rand();
array[j] = n;
}
printf("\n");break;
case RANDOM:
for ( i = 0; i <= dimensione ; i++) {
array[i] = rand();
}break;
case ESCI:
break;
default:
break;
}
return array;
}
int perno(int* array, int primo, int ultimo){
int i=primo;
int j=ultimo+1;
int supp=0;
int pivot=array[primo];
while(i<j){
do{
i=i+1;
qconfronti++;
}while(array[i]<=pivot);
do{
j=j-1;
qconfronti++;
}while(array[j]>pivot);
if(i<j){
supp=array[i];
array[i]=array[j];
array[j]=supp;
qscambi++;
}
}
supp=array[primo];
array[primo]=array[j];
array[j]=supp;
qscambi++;
return j;
}
void quickSort(int* array, int u, int v){
int q=0;
if(u==v){
return ;
}
q=perno(array, u, v);
if(u<q){
quickSort(array, u, q-1);
}
if(q<v){
quickSort(array, q+1, v);
}
}
void calculateQuicktime(int *array, int dim){
clock_t start, end;
double t;
qconfronti = 0;
qscambi = 0;
start = clock();
quickSort(array,0, dim);
end = clock();
t = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("\nConfronti: %d \t Scambi: %d", qconfronti, qscambi);
printf("\nTempo impiegato per %d elementi : %lf secondi", dim, t);
}
void automaticQS() {
printf("\nQuick Sort");
printf("\n\nOrdinati:\n");
calculateQuicktime(generaArray(CINQUECENTO, ORINATO), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, ORINATO), MILLE);
calculateQuicktime(generaArray(DUEMILA, ORINATO), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, ORINATO), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, ORINATO), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, ORINATO), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, ORINATO), CINQUANTAMILA);
printf("\n\nParzialmente ordinati:\n");
calculateQuicktime(generaArray(CINQUECENTO, PARZ_ORDINATO), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, PARZ_ORDINATO), MILLE);
calculateQuicktime(generaArray(DUEMILA, PARZ_ORDINATO), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, PARZ_ORDINATO), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, PARZ_ORDINATO), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, PARZ_ORDINATO), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, PARZ_ORDINATO), CINQUANTAMILA);
printf("\n\nInversamente ordinati:\n");
calculateQuicktime(generaArray(CINQUECENTO, INVERS), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, INVERS), MILLE);
calculateQuicktime(generaArray(DUEMILA, INVERS), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, INVERS), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, INVERS), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, INVERS), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, INVERS), CINQUANTAMILA);
printf("\n\nCasuali:\n");
calculateQuicktime(generaArray(CINQUECENTO, RANDOM), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, RANDOM), MILLE);
calculateQuicktime(generaArray(DUEMILA, RANDOM), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, RANDOM), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, RANDOM), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, RANDOM), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, RANDOM), CINQUANTAMILA);
}
Question
Why is it that my program only prints out the ordered numbers array and then crashes giving me exit code 11?
Ps. I use CLion
You have a few bugs ...
The bug you're currently experiencing ...
In perno
, although you have while (i < j)
, this does not prevent the do/while
loop for i
from going beyond the end of the array.
The segfault occurs when primo
is 500 and ultimo
is 1000, but i
will reach 333800.
I added a fix to the i
and j
loops to stop when going out of bounds. This may or may not be the correct fix.
After applying that fix and rerunning the program, you [seem to] recurse indefinitely. quickSort
is called with v
set to 1000, but u
is recursively set to u + 1
(perhaps the pivot value is not set well).
Eventually, you recurse infinitely with a u
value of 0 and a v
value of 1
You return early from quickSort
on if (u == v)
. That [probably] isn't a good enough test.
I tried if ((v - u) < 2)
instead.
Now it [eventually] recurses infinitely with u==251
and v==455
.
This indicates that the if (u < q)
and/or if (q < v)
tests in quickSort
are [probably] not adequate to prevent the infinite recursion.
I added some assert
calls there to [at least] abort early on the first such recursion.
When you allocate an array with a dimension (e.g. 500
), you allocate that many elements.
But, passing this value to quickSort
is incorrect. That's because the third quickSort
argument is the index of the last element and not the length.
So, in calculateQuicktime
, you want:
quickSort(array,0,dim - 1);
Side note: calculateQuicktime
does not call free
on array
, so you are leaking memory [I added a free
call].
Anyway, here's my [somewhat] refactored version of your code. I added debug printing with a dbgprt
macro.
But, I really found the above bugs mostly by running under gdb
.
Although this code is not a complete fix, it should give you some ideas:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <assert.h>
#define CINQUECENTO 500
#define MILLE 1000
#define DUEMILA 2000
#define CINQUEMILA 5000
#define DIECIMILA 10000
#define VENTIMILA 20000
#define CINQUANTAMILA 50000
typedef enum { ORINATO, INVERS, PARZ_ORDINATO, RANDOM } Ordine;
typedef enum { FIVEH, ONET, TWOT, FIVET, TENT, TWENTYT, FIFTYT } Dimensione;
int qconfronti = 0,
qscambi = 0;
int *generaArray(int dimensione, Ordine ordine);
int perno(int *array, int primo, int ultimo);
void quickSort(int *array, int u, int v);
void calculateQuicktime(int *array, int dim);
void automaticQS();
#ifdef DEBUG
#define dbgprt(_lvl,_fmt...) \
do { \
if (opt_d >= _lvl) \
printf(_fmt); \
} while (0)
#else
#define dbgprt(_fmt...) \
do { \
} while (0)
#endif
int opt_d;
int svlvl;
void
dbgpush(int newlvl)
{
svlvl = opt_d;
if (opt_d)
opt_d = newlvl;
}
void
dbgpop(void)
{
opt_d = svlvl;
}
int
main(int argc,char **argv)
{
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = (*cp != 0) ? atoi(cp) : 1;
break;
}
}
setlinebuf(stdout);
automaticQS();
return 0;
}
int *
generaArray(int dimensione, Ordine ordine)
{
int i,
j,
n;
int *array = malloc(dimensione * sizeof(int));
if (!array) {
return NULL;
}
printf("generaArray: %d\n",dimensione);
switch (ordine) {
case ORINATO:
for (i = 0; i < dimensione; i++) {
array[i] = i;
}
break;
case INVERS:
n = 0;
for (i = dimensione - 1; i >= 0; i--) {
array[i] = n;
n++;
}
break;
case PARZ_ORDINATO:
for (i = 0; i < dimensione / 2; i++) {
array[i] = i;
}
for (j = i + 1; j < dimensione; j++) {
n = rand();
array[j] = n;
}
printf("\n");
break;
case RANDOM:
for (i = 0; i <= dimensione; i++) {
array[i] = rand();
}
break;
#if 0
case ESCI:
break;
#endif
default:
break;
}
return array;
}
int
perno(int *array, int primo, int ultimo)
{
int i = primo;
int j = ultimo + 1;
int supp = 0;
int pivot = array[primo];
dbgprt(1,"perno: ENTER primo=%d ultimo=%d pivot=%d\n",primo,ultimo,pivot);
while (i < j) {
dbgprt(2,"perno: OUTLOOP i=%d j=%d\n",i,j);
do {
i = i + 1;
#if FIX1
if (i >= j)
break;
#endif
dbgprt(3,"perno: ILOOP i=%d array=%d\n",i,array[i]);
qconfronti++;
} while (array[i] <= pivot);
do {
#if FIX1
if (i >= j)
break;
#endif
j = j - 1;
dbgprt(3,"perno: JLOOP j=%d array=%d\n",j,array[j]);
qconfronti++;
} while (array[j] > pivot);
if (i < j) {
dbgprt(2,"perno: SWAP\n");
supp = array[i];
array[i] = array[j];
array[j] = supp;
qscambi++;
}
}
supp = array[primo];
array[primo] = array[j];
array[j] = supp;
qscambi++;
dbgprt(1,"perno: EXIT j=%d\n",j);
return j;
}
void
quickSort(int *array, int u, int v)
{
int q = 0;
dbgprt(1,"quickSort: ENTER u=%d v=%d\n",u,v);
#if ! FIX2
if (u == v) {
#else
if ((v - u) < 2) {
#endif
dbgprt(1,"quickSort: EXIT\n");
return;
}
q = perno(array, u, v);
if (u < q) {
assert(! ((q - 1) == v));
quickSort(array, u, q - 1);
}
if (q < v) {
assert(! ((q + 1) == u));
quickSort(array, q + 1, v);
}
dbgprt(1,"quickSort: EXIT\n");
}
void
calculateQuicktime(int *array, int dim)
{
clock_t start,
end;
double t;
qconfronti = 0;
qscambi = 0;
start = clock();
quickSort(array, 0, dim - 1);
end = clock();
t = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("\nConfronti: %d \t Scambi: %d", qconfronti, qscambi);
printf("\nTempo impiegato per %d elementi : %lf secondi", dim, t);
// NOTE/BUG: array is never freed (i.e. a memory leak)
#if 1
free(array);
#endif
}
void
automaticQS()
{
printf("\nQuick Sort");
printf("\n\nOrdinati:\n");
dbgpush(2);
calculateQuicktime(generaArray(CINQUECENTO, ORINATO), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, ORINATO), MILLE);
calculateQuicktime(generaArray(DUEMILA, ORINATO), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, ORINATO), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, ORINATO), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, ORINATO), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, ORINATO), CINQUANTAMILA);
dbgpop();
dbgpush(3);
printf("\n\nParzialmente ordinati:\n");
calculateQuicktime(generaArray(CINQUECENTO, PARZ_ORDINATO), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, PARZ_ORDINATO), MILLE);
calculateQuicktime(generaArray(DUEMILA, PARZ_ORDINATO), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, PARZ_ORDINATO), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, PARZ_ORDINATO), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, PARZ_ORDINATO), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, PARZ_ORDINATO), CINQUANTAMILA);
dbgpop();
printf("\n\nInversamente ordinati:\n");
calculateQuicktime(generaArray(CINQUECENTO, INVERS), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, INVERS), MILLE);
calculateQuicktime(generaArray(DUEMILA, INVERS), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, INVERS), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, INVERS), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, INVERS), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, INVERS), CINQUANTAMILA);
printf("\n\nCasuali:\n");
calculateQuicktime(generaArray(CINQUECENTO, RANDOM), CINQUECENTO);
calculateQuicktime(generaArray(MILLE, RANDOM), MILLE);
calculateQuicktime(generaArray(DUEMILA, RANDOM), DUEMILA);
calculateQuicktime(generaArray(CINQUEMILA, RANDOM), CINQUEMILA);
calculateQuicktime(generaArray(DIECIMILA, RANDOM), DIECIMILA);
calculateQuicktime(generaArray(VENTIMILA, RANDOM), VENTIMILA);
calculateQuicktime(generaArray(CINQUANTAMILA, RANDOM), CINQUANTAMILA);
}
UPDATE:
The above was done just with gdb
but not desk checking your code against a working [Hoare] algorithm.
So, using the wikipedia entry for quicksort: https://en.wikipedia.org/wiki/Quicksort a few more bug fixes ...
In perno
, the i
loop was failing for two reasons:
i
was int i = primo;
instead of int i = primo - 1;
This caused the loop to skip over [ignore] array[primo]
as a candidate. See: ORIG3
i
loop termination condition was while (array[i] <= pivot)
instead of while (array[i] < pivot)
. See: ORIG5
Note that your j
loop did not have this issue.With these fixes, the extra bounds checks I added in the i
and j
loops are no longer necessary (See: ORIG1
).
The reason that quickSort
would recurse infinitely was the fact that the recursive call inside it was: quickSort(array,u,q-1);
instead of quickSort(array,u,q);
See: ORIG4
I added a check in calculateQuicktime
to check for the final array actually being sorted.
With this check, some tests wouldn't crash [or recurse infinitely], but the final array would not be in sort.
This was because my [attempted] fix of changing if (u == v)
into if ((v - u) < 2
was incorrect. Your original code was correct. See: ORIG2
Some additional stylistic suggestions:
While using a #define
for certain constants is a good idea (e.g. #define PI 3.14159
), it's less readable for simple numbers (e.g. #define ONE 1
and #define TWO 2
). So, I'd eliminate CINQUECENTO
, etc.
I'd replace this with a static/global array for the test lengths (e.g. testlen
).
And, even though they weren't used, the same goes for FIVEH
, etc.
And, the test code in automaticQS
is cumbersome. Each section is replicating a fair amount of the code. By creating a helper function (e.g. testall
), the test code can be greatly simplified.
Anyway, here's the fully working code with all the additional changes:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <assert.h>
#ifndef ORIG1
#define ORIG1 1
#endif
#ifndef ORIG2
#define ORIG2 1
#endif
#ifndef ORIG3
#define ORIG3 0
#endif
#ifndef ORIG4
#define ORIG4 0
#endif
#ifndef ORIG5
#define ORIG5 0
#endif
#if 0
#define CINQUECENTO 500
#define MILLE 1000
#define DUEMILA 2000
#define CINQUEMILA 5000
#define DIECIMILA 10000
#define VENTIMILA 20000
#define CINQUANTAMILA 50000
#else
int testlen[] = {
500, 1000, 2000, 5000, 10000, 20000, 50000, -1
};
#endif
typedef enum { ORINATO, INVERS, PARZ_ORDINATO, RANDOM } Ordine;
#if 0
typedef enum { FIVEH, ONET, TWOT, FIVET, TENT, TWENTYT, FIFTYT } Dimensione;
#endif
int qconfronti = 0, qscambi = 0;
int *generaArray(int dimensione, Ordine ordine);
int perno(int *array, int primo, int ultimo);
void quickSort(int *array, int u, int v);
void calculateQuicktime(int *array, int dim);
void automaticQS();
#ifdef DEBUG
#define dbgprt(_lvl,_fmt...) \
do { \
if (opt_d >= _lvl) \
printf(_fmt); \
} while (0)
#else
#define dbgprt(_fmt...) \
do { \
} while (0)
#endif
int opt_d;
int svlvl;
void
dbgpush(int newlvl)
{
svlvl = opt_d;
if (opt_d)
opt_d = newlvl;
}
void
dbgpop(void)
{
opt_d = svlvl;
}
int
main(int argc,char **argv)
{
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = (*cp != 0) ? atoi(cp) : 1;
break;
}
}
setlinebuf(stdout);
automaticQS();
return 0;
}
int *
generaArray(int dimensione, Ordine ordine)
{
int i, j, n;
int *array = malloc(dimensione * sizeof(int));
if (array == NULL)
return NULL;
dbgprt(1,"generaArray: %d\n",dimensione);
switch (ordine) {
case ORINATO:
for (i = 0; i < dimensione; i++) {
array[i] = i;
}
break;
case INVERS:
n = 0;
for (i = dimensione - 1; i >= 0; i--) {
array[i] = n;
n++;
}
break;
case PARZ_ORDINATO:
for (i = 0; i < dimensione / 2; i++) {
array[i] = i;
}
for (j = i + 1; j < dimensione; j++) {
n = rand();
array[j] = n;
}
printf("\n");
break;
case RANDOM:
for (i = 0; i <= dimensione; i++)
array[i] = rand();
break;
#if 0
case ESCI:
break;
#endif
default:
break;
}
return array;
}
int
perno(int *array, int primo, int ultimo)
{
#if ORIG3
int i = primo;
#else
int i = primo - 1;
#endif
int j = ultimo + 1;
int supp = 0;
int pivot = array[primo];
dbgprt(1,"perno: ENTER primo=%d ultimo=%d pivot=%d\n",primo,ultimo,pivot);
while (i < j) {
dbgprt(2,"perno: OUTLOOP i=%d j=%d\n",i,j);
do {
i = i + 1;
#if (! ORIG1)
if (i >= j)
break;
#endif
dbgprt(3,"perno: ILOOP i=%d array=%d\n",i,array[i]);
qconfronti++;
#if ORIG5
} while (array[i] <= pivot);
#else
} while (array[i] < pivot);
#endif
do {
#if (! ORIG1)
if (i >= j)
break;
#endif
j = j - 1;
dbgprt(3,"perno: JLOOP j=%d array=%d\n",j,array[j]);
qconfronti++;
} while (array[j] > pivot);
if (i < j) {
dbgprt(2,"perno: SWAP\n");
supp = array[i];
array[i] = array[j];
array[j] = supp;
qscambi++;
}
}
supp = array[primo];
array[primo] = array[j];
array[j] = supp;
qscambi++;
dbgprt(1,"perno: EXIT j=%d\n",j);
return j;
}
void
quickSort(int *array, int u, int v)
{
int q = 0;
dbgprt(1,"quickSort: ENTER u=%d v=%d\n",u,v);
#if ORIG2
if (u == v) {
#else
if ((v - u) < 2) {
#endif
dbgprt(1,"quickSort: EXIT\n");
return;
}
q = perno(array, u, v);
if (u < q) {
assert((q - 1) != v);
#if ORIG4
quickSort(array, u, q - 1);
#else
quickSort(array, u, q);
#endif
}
if (q < v) {
assert((q + 1) != u);
quickSort(array, q + 1, v);
}
dbgprt(1,"quickSort: EXIT\n");
}
void
calculateQuicktime(int *array, int dim)
{
clock_t start, end;
double t;
qconfronti = 0;
qscambi = 0;
start = clock();
quickSort(array, 0, dim - 1);
end = clock();
t = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("Confronti: %d \t Scambi: %d\n", qconfronti, qscambi);
printf("Tempo impiegato per %d elementi : %lf secondi\n", dim, t);
// check array to ensure it's sorted
int err = 0;
int old = array[0];
for (int idx = 1; idx < dim; ++idx) {
int cur = array[idx];
if (cur < old) {
printf("calculateQuicktime: BADSORT idx=%d old=%d cur=%d\n",
idx,old,cur);
err = 1;
}
old = cur;
}
if (err)
exit(1);
// NOTE/BUG: array is never freed (i.e. a memory leak)
#if 1
free(array);
#endif
}
void
testall(Ordine order,const char *reason)
{
printf("\n\n%s:\n",reason);
for (int *len = testlen; *len >= 0; ++len) {
printf("\n");
int *arr = generaArray(*len,order);
calculateQuicktime(arr,*len);
}
}
void
automaticQS()
{
printf("\nQuick Sort");
testall(ORINATO,"Ordinati");
testall(PARZ_ORDINATO,"Parzialmente ordinati");
testall(INVERS,"Inversamente ordinati");
testall(RANDOM,"Casuali");
}