C randomized pivot quicksort (improving the partition function)

后端 未结 2 564
小鲜肉
小鲜肉 2021-01-13 22:23

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 wor

相关标签:
2条回答
  • 2021-01-13 22:43

    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)
    
    0 讨论(0)
  • 2021-01-13 22:55

    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.

    0 讨论(0)
提交回复
热议问题