user3236524
user3236524

Reputation: 51

C randomized pivot quicksort (improving the partition function)

I'm a computer science student (just started), I was working on writing from pseudocode a randomized pivot version of Quicksort. I've written and tested it, and it all works perfectly however...

The partition part looks a bit too complicated, as it feels I have missed something or overthought it. I can't understand if it's ok or if I made some avoidable mistakes.

So long story short: it works, but how to do better?

Thanks in advance for all the help

void partition(int a[],int start,int end)
{
    srand (time(NULL));
    int pivotpos = 3;   //start + rand() % (end-start);
    int i = start;    // index 1
    int j = end;      // index 2
    int flag = 1;
    int pivot = a[pivotpos];   // sets the pivot's value
    while(i<j && flag)      // main loop
    {
        flag = 0;
        while (a[i]<pivot)
        {
            i++;
        }
        while (a[j]>pivot)
        {
            j--;
        }
        if(a[i]>a[j]) // swap && sets new pivot, and restores the flag
        {
            swap(&a[i],&a[j]);
            if(pivotpos == i)
                pivotpos = j;
            else if(pivotpos == j)
                pivotpos = i;
            flag++;
        }
        else if(a[i] == a[j])       // avoids getting suck on a mirror of values (fx pivot on pos 3 of : 1-0-0-1-1)
        {
            if(pivotpos == i) 
                j--;
            else if(pivotpos == j)
                i++;
            else
            {
                i++;
                j--;
            }
            flag++;
        }
    }
}

Upvotes: 5

Views: 19527

Answers (2)

jfly
jfly

Reputation: 7990

This is the pseudo code of partition() from Introduction to Algorithms , which is called Lomuto's Partitioning Algorithm, and there's a good explanation below it in the book.

PARTITION(A, p, r)
1 x ← A[r]
2 i ← p - 1
3 for j ← p to r - 1
4   do if A[j] ≤ x
5       then i ←i + 1
6           exchange A[i] ↔ A[j]
7 exchange A[i + 1] ↔ A[r]
8 return i +1

You can implement a randomized partition implementation easily based on the pseudo code above. As the comment pointed out, move the srand() out of the partition.

// srand(time(NULL));
int partition(int* arr, int start, int end)
{
    int pivot_index = start + rand() % (end - start + 1);
    int pivot = arr[pivot_index ];

    swap(&arr[pivot_index ], &arr[end]); // swap random pivot to end.
    pivot_index = end;
    int i = start -1;

    for(int j = start; j <= end - 1; j++)
    {
        if(arr[j] <= pivot)
        {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    swap(&arr[i + 1], &arr[pivot_index]); // place the pivot to right place

    return i + 1;
}

And there is another partition method mentioned in the book, which is called Hoare's Partitioning Algorithm, the pseudo code is as below:

Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
    repeat
        j = j - 1
    until A[j] <= x
    repeat
        i = i + 1
    until A[i] >= x
    if i < j
        swap( A[i], A[j] )
    else
        return j

After the partition, every element in A[p...j] ≤ every element in A[j+1...r]. So the quicksort would be:

QUICKSORT (A, p, r)
if p < r then
 q = Hoare-Partition(A, p, r)
 QUICKSORT(A, p, q)
 QUICKSORT(A, q+1, r)

Upvotes: 4

WhozCraig
WhozCraig

Reputation: 66224

There are multiple ways to partition for quicksort, the following being likely the simplest I can muster. Generally two schools of partitioning are used:

  1. The Squeeze - collapses both ends of the sequence until a suitable swap pair is found, then swaps two elements into proper sides of the partition. Not trivial to implement, but can be more efficient (reduced swap count) than the alternative...
  2. The Sweep - uses a single left to right (or right to left) sweep the values, swapping values to an incrementing pivot index that moves as the algorithm runs. Very simple to implement, as you'll see below.

I prefer the Sweep algorithm for people learning quicksort and partitioning only because it is so dead-simple to implement. Both can be implemented to perform in-place partitioning, as is the case in the implementation below. At no time except in swap() will you see a value stored in temp-storage.

Using a random pivot selection is only a small part of this. The following shows how to initialize the random number generator, and demonstrates likely the simplest partition algorithm and quicksort usage therein you're going to find.

It demonstrates, among other things, that in C/C++, you don't need both ends of a partition since simple pointer arithmetic can be used to adjust the "top" half of a partition. See the quicksort() function for how this is done.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void swap(int *lhs, int *rhs)
{
    if (lhs == rhs)
        return;

    int tmp = *lhs;
    *lhs = *rhs;
    *rhs = tmp;
}

int partition(int ar[], int len)
{
    int i, pvt=0;

    // swap random slot selection to end.
    //  ar[len-1] will hold the pivot value.
    swap(ar + (rand() % len), ar+(len-1));
    for (i=0; i<len; ++i)
    {
        if (ar[i] < ar[len-1])
            swap(ar + i, ar + pvt++);
    }

    // swap the pivot value into position
    swap(ar+pvt, ar+(len-1));
    return pvt;
}

void quicksort(int ar[], int len)
{
    if (len < 2)
        return;

    int pvt = partition(ar, len);
    quicksort(ar, pvt++); // note increment. skips pivot slot
    quicksort(ar+pvt, len-pvt);
}


int main()
{
    srand((unsigned int)time(NULL));

    const int N = 20;
    int data[N];

    for (int i=0; i<N; ++i)
    {
        data[i] = rand() % 50 + 1;
        printf("%d ", data[i]);
    }
    puts("");

    quicksort(data, N);

    for (int i=0; i<N; ++i)
        printf("%d ", data[i]);

    puts("");

    return 0;
}

Output (varies, obviously)

32 49 42 49 5 18 41 48 22 33 40 27 12 47 41 6 50 27 8 7 
5 6 7 8 12 18 22 27 27 32 33 40 41 41 42 47 48 49 49 50 

Note: this does NOT account for modulo bias for using rand() % len, and frankly it would be overkill to do so for this example. If it were critical, I would use another generator entirely. An outstanding discussion for methods of choosing random pivot locations for quicksort partitioning can be found at this post on this site, including many links to different methods. I suggest reviewing it.

Upvotes: 4

Related Questions