I have a problem in my pretty easy algorithm - quicksort in C.
It is very efficient (about 0.1s with randomize and checking if the list is sorted) but when i want to sort more than 500k elements it crashes.
Unfortunatelly i need to sort more of them because i need to write some kind of summary at the end :(
Here is my code, maybe someone will see a stupid mistake.
Thanks in advance!
int quick (int a[],int begin,int end)
{
int i = begin, j = end, w, q, pivot, k;
q=begin+end;
q=q/2;
pivot=a[q];
while (1)
{
while (a[j] > pivot && j>=0)
j=j-1;
while (a[i] < pivot && i<j)
i=i+1;
if (i < j)
{
k = a[i];
a[i] = a[j];
a[j] = k;
i++;
j--;
}
else
return j;
}
}
void quicks (int a[], int begin, int end)
{
int x;
if (end>begin)
{
x=quick(a,begin,end);
quicks(a,begin,x);
quicks(a,x+1,end);
}
}
It seems that i just need to use malloc and it is working fine. Thanks a lot for Your help!
You are suffering from RAM exhaustion/rollover: As you use an array of int, each of them requires 4 bytes. Your memory mapping is handled using size_t-type indexes. If you are compiling in 32-bit mode (which is probably your case), the maximum number it can get at is 2147483648 (2^31). With 4 bytes per int, you can only handle 536870912 elements (2^31 / 4).
As the system requires some RAM for other purposes (e.g. globals), you can only use a bit more than 500K entries.
Solution: Use a 64-bit compiler and you should be fine.
BR
Here is another and simpler implementation.
void quickSort(int a[], int begin, int end)
{
int left = begin - 1, right = end + 1, tmp;
const int pivot = a[(begin+end)/2];
if (begin >= end)
return;
while(1)
{
do right--; while(a[right] > pivot);
do left++; while(a[left] < pivot);
if(left < right)
{
tmp = a[left];
a[left] = a[right];
a[right] = tmp;
}
else
break;
}
quickSort(a, begin, right);
quickSort(a, right+1, end);
}
You call it like this
int main(void)
{
int tab[5] = {5, 3, 4, 1, 2};
int i;
quickSort(tab, 0, 4); // 4 is index of lest element of tab
for(i = 0; i < 5; i++)
printf("%d ", tab[i]);
printf("\n");
return 0;
}
Related
This is my code for merge sort in C. I'm not able to understand what the issue is here. My knowledge of pointers is not that much. The merge function takes in 2 arrays and merges them. the sort function is a recursive function which is supposed to sort the array.
int * merge(int *fir, int n, int *sec, int m){
int res[m+n];
int x=0, y=0;
for(int i = 0; i < m+n; i++){
if(*(fir+x)<=*(sec+y)){
res[i] = *(fir+x);
x++;
}else{
res[i] = *(sec+y);
y++;
}
}
return res;
}
int * sort(int A[], int n){
if(n == 1){
return A;
}
int mid = n/2;
int AL[mid], AR[n-mid];
for(int i = 0; i < mid; i++){
AL[i] = A[i];
}
for(int i = 0; i < n-mid; i++){
AR[i] = A[i+mid];
}
int *BL, *BR;
BL = sort(AL, mid);
BR = sort(AR, n-mid);
return(merge(BL, mid, BR, n-mid));
}
int main(){
int n;
scanf("%d", &n);
int A[n];
for(int i = 0; i < n; i++){
scanf("%d", &A[i]);
}
int *sortedArray;
sortedArray = sort(A, n);
for(int i = 0; i < n; i++){
printf("%d ", *(sortedArray+i));
}
return 0;
}
And this is the output
q8.c:16:9: warning: address of stack memory associated with local variable 'res' returned [-Wreturn-stack-address]
return res;
^~~
1 warning generated.
7
23 12 56 67 11 99 97
97 32766 539779418 32767 -2002825496 32767 6 %```
There are two issues here: First, you merge your partial arrays into a temporary local array, which yoes out of bounds after you return from merge. The pointer you return points to invlid memory. That's what the warning about.
Second, you don't check whether you are reading beyond the limits of the partial arrays when you merge: The condition x < n must be true when you access fir, likewise for y < m and sec.
You are also causing confusion by returning a pointer to the first element of the sorted or merged arrays. That suggests that you create new sorted and merged arrays (and that's what you try to do in merge). This is okay for garbage-collected morern languages, but C doesn't work that way.
In C, if you need new memory, you must allocate it and later free it explicitly. In a recursive function like yours, this is tedious, because you are interested only in the final sorted array, not in the intermediate results. Therefore, C sorting algorithms usually work "in place": The same memory is used thoughout the sorting and elements are swapped. The original order of the elements is lost unless you make a copy before sorting.
For merge sort, you need auxiliary memory. In your case, you use the temporary arrays AL and AR, which are copies of the contents of the original array, A. Now when you merge, you can merge AL and AR back into A.
So istead of creating a ephemeral local array, pass in A so that it can be filled with the sorted elements:
void sort(int A[], int n)
{
if (n > 1) {
int mid = n / 2;
int AL[mid], AR[n - mid];
for (int i = 0; i < mid; i++) AL[i] = A[i];
for (int i = 0; i < n - mid; i++) AR[i] = A[i + mid];
sort(AL, mid);
sort(AR, n - mid);
merge(A, AL, mid, AR, n - mid);
}
}
Your merge function is now very similar to the one you has before, only that you have the result array as parameter and that you must catch the out-of-bound cases before accessing elements with [].
void merge(int *res, const int *fir, int n, const int *sec, int m)
{
int x = 0, y = 0;
for(int i = 0; i < m + n; i++) {
if (x == n) res[i] = sec[y++];
else if (y == m) res[i] = fir[x++];
else if (fir[x] <= sec[y]) res[i] = fir[x++];
else res[i] = sec[y++];
}
}
I'm getting a segmentation fault:11 when I try to run this QuickSort, but it compiles fine. I'm running it with a driver, which is why I use quicksort() and quicksortR(). What's causing the segmentation?
/* -------- Quick sort stuff starts here --------- */
int partition(int array[], int start, int stop) {
int compars = 0;
int pivot = array[stop];
int i = start - 1;
for (int j = start; j <= stop - 1; j++) {
if (array[j] <= pivot) {
i++;
int temp = array[j];//swaps values of i and j
array[j] = array[i];
array[i] = temp;
}
compars++;
}
int temp = array[i + 1];
array[i + 1] = array[stop];
array[stop] = temp;
return compars;
}
int quickSortR(int array[], int start, int stop) {
int compars = 0;
int mid = array[stop];
if (start < stop) {
compars = compars + partition(array, start, stop);
quickSortR(array, start, mid - 1);
quickSortR(array, mid+1, stop);
}
return compars;
}
int quickSort(int array[], int n) {
return quickSortR(array, 0, n);
}
/* ----------- end quick sort stuff ----------------- */
You call quicksort with the number of elements of the array as the stop argument, but you initialize the pivot as int pivot = array[stop];. You are reading past the end of the array. Undefined behavior.
There are probably other problems in the code, but this alone explains the crash.
I'm getting a segmentation fault:11 when I try to run this QuickSort, but it compiles fine
Please note: program compiling means compiler understands what you want your program to do. In no way compiler checks if what your program does makes any sense whatsoever.
If it knew what you want to do why wouldn't it write the program itself?
#include <stdio.h>
#define ARRAY_SIZE 10
void lomuto (int A[], int l, int r, int smallerAtLeft)
{
if (smallerAtLeft == 1) //move elements smaller than pivot to the left and the greater ones to the right
{
int tmp, tmp2,pivot,i,j;
pivot = A[r];
i = l-1;
for (j =0; j<r-1; j++)
{
if (A[j] <= pivot)
{
i++;
tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
tmp2 = A[i+1];
A[i+1] = A[r];
A[r] = tmp2;
}
if (smallerAtLeft == 0) //move elements smaller than pivot to the right and the greater ones to the left
{
int tmp3, tmp4,pivot,i,j;
pivot = A[r];
i = l-1;
for (j=0; j<r-1; j++)
{
if (A[j]>= pivot)
{
i++;
tmp3 = A[i];
A[i] = A[j];
A[j] = tmp3;
}
}
tmp4 = A[i+1];
A[i+1] = A[r];
A[r] = tmp4;
}
}
void quicksort (int A[], int l, int r, int ascending)
{
lomuto (A,l,r,ascending);
}
int main()
{
int testarray;
int testArray[ARRAY_SIZE] = {4, 2, 5, 3, 6, 7, 8, 1, 0};
quicksort (testarray,0,8,1);
return testarray;
}
Good evening.
Usually I search almost every forum and deepest threads for dubiety in my codes.
But this time I did not found an answer that could help me. I would be so thankful if anyone could tell my why the code-exe stops working but during compiling there is no error showing onscreen.
We have to implement the quicksort algorithm with the lomuto-partitioning. If the variable "smallerAtLeft" ist equal to 1, the array should be ordered by an increasing property and if its equal to 0 decreasingly.
Furthermore we have to implement to void functions like you see in the code. The "lomuto-fct" and the "quicksort-fct" that contains the lomuto one.
Maybe this Reverse-Lomuto-Thread will help some other people too in the future..
I don't think you understand what the return value from main is and what it's used for. It is usually an indicator of success and failure, with the typical values 0 for success and a small positive value for failure. There are even macros defined for this purpose in the <stdlib.h> header file: EXIT_SUCCESS and EXIT_FAILURE.
If you want to see the sorted array you need to print it:
printf("Sorted array = {");
for (unsigned i = 0; i < ARRAY_SIZE; ++i)
{
printf(" %d", testArray[i]);
}
printf(" }\n");
That of course requires you to pass the actual array to your sorting function.
I'm trying to implement merge sort in C using arrays, here's my code:
#include <stdio.h>
#include <stdlib.h>
void merge(int s[], int low, int middle, int high)
{
int i,l=0,r=0;
int left[high/2], right[high/2];
for(i = low; i<=middle; i++) left[i-low] = s[i];
for(i = middle+1; i<=high; i++) right[i-middle-1] = s[i];
i = low;
while(l <= middle-low || r <= high - middle - 1)
{
if(left[l] <= right[r])
{
s[i++] = left[l];
l++;
}
else
{
s[i++] = right[r];
r++;
}
}
while(l <= middle-low)
{
s[i++] = left[l];
l++;
}
while(r <= high - middle - 1)
{
s[i++] = left[r];
r++;
}
}
void mergesort(int s[], int low, int high)
{
int i;
int middle;
if(low < high){
middle = (low + high)/2;
mergesort(s, low, middle);
mergesort(s, middle+1, high);
merge(s, low, middle, high);
}
}
int main()
{
int nums[] = {5, 345, 1, 120, 40, 3450};
int size = (sizeof(nums))/(sizeof(int));
int i;
for(i = 0; i < size; i++)
printf("%d ", nums[i]);
printf("\n");
mergesort(nums, 0, size);
for(i = 0; i < size; i++)
printf("%d ", nums[i]);
printf("\n");
return 0;
}
That outputs:
5 345 1 120 40 3450
0 1 4 5 40 120
Which is kind of close. Could someone point out my mistakes? Thank you.
You access the array out of bounds at several places. Your code uses C-style ranges, which have an inclusive lower bound L and an exclusive upper bound H. Exclusive means that the upper bound H is not a valid index in the (sub-)array. A typical loop over the range look like this:
for (i = L; i < U; i++) ...
or
i = L;
while (i < U) ...
A greater-than-or-equal operator <= in such loops should make you wary, as should suprious additions or subtraction of 1. They might be correct in some cases, but they are usually consequences of inconsitent array indexing.
Let's revise your code with the C-style ranges in mind:
int left[high/2], right[high/2];
The array sizes are wrong. The left array has middle - low elements and the right array has high - middle elements. If the array size high - low is odd, you have one more element in right than in left.
for(i = low; i<=middle; i++) left[i-low] = s[i];
You mistakenly put the middle element in the left array. It is the first element of the right array.
for(i = middle+1; i<=high; i++) right[i-middle-1] = s[i];
Same here, plus you access s[high] which is one beyond the array.
i = low;
while(l <= middle-low || r <= high - middle - 1)
The conditions should have < and no -1. More importantly, the conditions should both be true, otherwise you access the subarrays out of bounds; hence the operator should be ยด&&`.
if(left[l] <= right[r])
The <= is okay, though, for once.
while(l <= middle-low)
{
s[i++] = left[l];
l++;
}
while(r <= high - middle - 1)
{
s[i++] = left[r];
r++;
}
Here, it should be < again. Also note that you access left with the index r, which is probably just a typo owed to copy and paste.
if(low < high){
middle = (low + high)/2;
mergesort(s, low, middle);
mergesort(s, middle+1, high);
merge(s, low, middle, high);
}
Here, the second call to megesort should be to middle, not to middle + 1. Because the upper bound is exclusive and the lower is not, adjacent arrays share the same bounds.
Here's a sort that works:
void merge(int s[], int low, int middle, int high)
{
int i, l = 0, r = 0;
int left[middle - low];
int right[high - middle];
for (i = low; i < middle; i++) left[i - low] = s[i];
for (i = middle; i < high; i++) right[i - middle] = s[i];
i = low;
while (low + l < middle && middle + r < high) {
if (left[l] < right[r]) {
s[i++] = left[l];
l++;
} else {
s[i++] = right[r];
r++;
}
}
while (low + l < middle) {
s[i++] = left[l];
l++;
}
while (middle + r < high) {
s[i++] = right[r];
r++;
}
}
void mergesort(int s[], int low, int high)
{
int middle;
if (low + 1 < high) {
middle = (low + high) / 2;
mergesort(s, low, middle);
mergesort(s, middle, high);
merge(s, low, middle, high);
}
}
The code can still be improved. The different indices for the left and right subarrays make it difficult to maintain and test the code. If you have already learned about pointer arithmetic, you can do without the low bound entirely by passing array + low and the size as new array base, as EOF has suggested in a comment.
M Oehm provided an explanation and a fixed example of the original code in his answer.
Here is an alternate version that does a one time allocation of the temporary array and uses a pair of co-recursive functions to avoid copying of data. I'm not sure why top down merge sort is used so often, bottom up merge sort is non-recursive, a little bit faster, and simpler to understand.
On my system, Intel 2600K 3.4ghz, this example can sort 20 million 32 bit integers in about 2 seconds. (A bottom up merge sort would take about 1.9 seconds).
void TopDownSplitMergeAtoA(int a[], int b[], size_t ll, size_t ee);
void TopDownSplitMergeAtoB(int a[], int b[], size_t ll, size_t ee);
void MergeRuns(int a[], int b[], size_t ll, size_t rr, size_t ee);
void TopDownMergeSort(int a[], size_t n)
{
int *b;
if(n < 2) // if size < 2 return
return;
b = malloc(n * sizeof(int)); // one time allocation
TopDownSplitMergeAtoA(a, b, 0, n);
free(b);
return;
}
void TopDownSplitMergeAtoA(int a[], int b[], size_t ll, size_t ee)
{
size_t rr;
if((ee - ll) == 1) // if size == 1 return
return;
rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoB(a, b, ll, rr);
TopDownSplitMergeAtoB(a, b, rr, ee);
MergeRuns(b, a, ll, rr, ee); // merge b to a
}
void TopDownSplitMergeAtoB(int a[], int b[], size_t ll, size_t ee)
{
size_t rr;
if((ee - ll) == 1){ // if size == 1 copy a to b
b[ll] = a[ll];
return;
}
rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMergeAtoA(a, b, ll, rr);
TopDownSplitMergeAtoA(a, b, rr, ee);
MergeRuns(a, b, ll, rr, ee); // merge a to b
}
void MergeRuns(int a[], int b[], size_t ll, size_t rr, size_t ee)
{
size_t o = ll; // b[] index
size_t l = ll; // a[] left index
size_t r = rr; // a[] right index
while(1){ // merge data
if(a[l] <= a[r]){ // 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
}
}
}
Newbie programmer here trying to implement quicksort, yet it won't work. I've looked at online resources but I just can't seem to spot the error in my implementation. Thanks in advance.
EDIT Issue I'm having seems like it gets stuck in the quicksort function, and the program just hangs. When I tried debugging it with printf's, the original array seems to have been modified with unexpected numbers (not from the original list), such as 0's.
void quicksort(int a[], const int start, const int end)
{
if( (end - start + 1 ) < 2)
return;
int pivot = a[rand()%(end - start)];
//Two pointers
int L = start;
int R = end;
while(L < R)
{
while(a[L] < pivot)
L++;
while(a[R] > pivot)
R--;
if(L < R)
swap(a,L,R);
}
quicksort(a, start, L-1);
quicksort(a, L+1, end );
}
void swap(int a[], const int pos1, const int pos2)
{
a[pos1] ^= a[pos2];
a[pos2] ^= a[pos1];
a[pos1] ^= a[pos2];
}
int main()
{
int array[20] = {0};
int size = sizeof(array)/sizeof(array[0]);//index range = size - 1
int i = 0;
printf("Original: ");
for (i; i < size; i++)
{
array[i] = rand()%100+ 1;
printf("%d ", array[i]);
}
printf("\n");
quicksort(array,0,size-1);
int j = 0;
printf("Sorted: ");
for(j; j < size; j++)
printf("%d ", array[j]);
printf("\n");
}
Additional Question: In regards to calling quicksort recursively, would the left and right pointer always point towards the pivot at the end of each partition? If so, is calling quicksort from start to L-1 and L+1 to end correct?
Also, is the if (L < R) before the swap necessary?
I believe that the problems stem from two errors in the logic. The first one is here:
int pivot = a[rand()%(end - start)];
Note that this always picks a pivot in the range [0, end - start) instead of [start, end). I think you want to have something like
int pivot = a[rand()%(end - start) + start];
so that you pick a pivot in the range you want.
The other error is in this looping code:
while(L < R)
{
while(a[L] < pivot)
L++;
while(a[R] > pivot)
R--;
if(L < R)
swap(a,L,R);
}
Suppose that L < R, but that a[L], a[R], and pivot are all the same value. This might come up, for example, if you were quicksorting a range containing duplicate elements. It also comes up when you use rand with the standard Linux implementation of rand (I tried this on my machine and 27 was duplicated twice). If this is the case, then you never move L or R, because the conditions in the loops always evaluate to false. You will need to update your logic for partitioning elements when duplicates are possible, since otherwise you'll go into an infinite loop here.
Hope this helps!
After the While statement, R should be less than L, try this:
quicksort(a, start, R);
quicksort(a, L, end );
And the statement if(L < R) is not necessary.