删除Yaroslavskiys Dual Pivot Quicksort中的递归

时间:2017-04-11 11:23:14

标签: c recursion qsort

我开始研究Yaroslavskiys Dual Pivot Quicksort的修改版本,作为第一步,我尝试删除递归(代码基于此paper中的递归版本)。这似乎有点老式,但我仍然想尝试一下。这是我到目前为止的C代码。它按预期工作,但我有点担心本地堆栈可能溢出的病态情况:

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

#define QS_STACK_SIZE     96
#define QS_ARR_SIZE       10000000

#define INT_SWAP(x_,y_)\
do {\
    int tmp_ = (x_);\
    (x_) = (y_);\
    (y_) = tmp_;\
} while(0)

void qsort_yaro(int *a, int left, int right, int div);

int main(void)
{
    int i;
    static int a[QS_ARR_SIZE];

    srand (time (NULL));

    for (i = 0; i < QS_ARR_SIZE; i ++)
        a[i] = rand();

    printf ("Sorting %i elements ...\n", QS_ARR_SIZE);
    qsort_yaro (a, 0, QS_ARR_SIZE - 1, 3);

    for (i = 0; i < QS_ARR_SIZE - 1; i ++)
        if (a[i] > a[i + 1])
            {
            printf ("ERROR : Sequence not ascending!\n");
            break;
            }

    if (i == QS_ARR_SIZE - 1)
        printf ("OK : Sequence ascending!\n");

    return 0;
}

void qsort_yaro(int *a, int left, int right, int div)
{
    int i, j, k, third;
    int len;
    int m1, m2;
    int pivot1, pivot2;
    int less, great;
    int dist;
    int hi[QS_STACK_SIZE];
    int lo[QS_STACK_SIZE];
    int lvl = -1;
    int max_stack = 0;

    lo[++lvl] = left;
    hi[lvl] = right;

    while (lvl >= 0)
        {
        left = lo[lvl];
        right = hi[lvl--];

        while ((len = right - left) >= 27)
            { // insertion sort for tiny array
            third = len / div;
            //
            // "medians"
            m1 = left + third;
            m2 = right - third;

            if (m1 <= left)
                m1 = left + 1;
            if (m2 >= right)
                m2 = right - 1;
            if (a[m1] < a[m2])
                {
                INT_SWAP(a[m1], a[left]);
                INT_SWAP(a[m2], a[right]);
                }
            else
                {
                INT_SWAP(a[m1], a[right]);
                INT_SWAP(a[m2], a[left]);
                }

            // pivots
            pivot1 = a[left];
            pivot2 = a[right];
            // pointers
            less = left + 1;
            great = right - 1;
            // sorting
            for (k = less; k <= great; k++)
                {
                if (a[k] < pivot1)
                    {
                    INT_SWAP(a[k], a[less]);
                    less++;
                    }
                else if (a[k] > pivot2)
                    {
                    while (k < great && a[great] > pivot2)
                        great--;
                    INT_SWAP(a[k], a[great]);
                    great--;
                    if (a[k] < pivot1)
                        {
                        INT_SWAP(a[k], a[less]);
                        less++;
                        }
                    }
                }
            // swaps
            dist = great - less;
            if (dist < 13)
                div++;

            INT_SWAP(a[less - 1], a[left]);
            INT_SWAP(a[great + 1], a[right]);

//            lo[++lvl] = left;
//            hi[lvl]   = less - 2;
//            lo[++lvl] = great + 2;
//            hi[lvl]   = right;

            // Push the smaller interval on top for less stack usage
            // subarrays
            if (less - 2 - left > right - great - 2)
                {
                lo[++lvl] = left;
                hi[lvl]   = less - 2;
                lo[++lvl] = great + 2;
                hi[lvl]   = right;
                }
            else
                {
                lo[++lvl] = great + 2;
                hi[lvl]   = right;
                lo[++lvl] = left;
                hi[lvl]   = less - 2;
                }
            if (max_stack < lvl)
                max_stack = lvl;
            // equal elements
            if (dist > len - 13 && pivot1 != pivot2)
                {
                for (k = less; k <= great; k++)
                    {
                    if (a[k] == pivot1)
                        {
                        INT_SWAP(a[k], a[less]);
                        less++;
                        }
                    else if (a[k] == pivot2)
                        {
                        INT_SWAP(a[k], a[great]);
                        great--;
                        if (a[k] == pivot1)
                            {
                            INT_SWAP(a[k], a[less]);
                            less++;
                            }
                        }
                    }
                }
            // subarray
            if (pivot1 < pivot2)
                {
                left  = less;
                right = great;
                }
            else
                {
                left  = lo[lvl];
                right = hi[lvl--];
                }
            }

        for (i = left + 1; i <= right; i++)
            for (j = i; j > left && a[j] < a[j - 1]; j--)
                INT_SWAP (a[j], a[j - 1]);
        }
    printf ("Max Stack Size : %i\n", max_stack);
}

我通过这个小小的改变调整了最初的实施

        if (less - 2 - left > right - great - 2)
            {
            lo[++lvl] = left;
            hi[lvl]   = less - 2;
            lo[++lvl] = great + 2;
            hi[lvl]   = right;
            }
        else
            {
            lo[++lvl] = great + 2;
            hi[lvl]   = right;
            lo[++lvl] = left;
            hi[lvl]   = less - 2;
            }

基于与传统迭代1-枢轴实现中相同的思想,其中较大的部分被推入堆栈以便将堆栈大小限制为log(n)。在这里,较小的部分放在堆栈的顶部,以便尽早将其弹回。

我使用随机生成的10.000.000元素数组进行了一些实验,实际上可以通过此方法保存多达5个堆栈级别(最多约45个)。

现在,我没有找到一个严格的论据,为什么在这个实现中堆栈大小应该被常量限制,所以要么我在这里监督数学,要么我需要一个更好的想法和实现。

0 个答案:

没有答案