快速排序 - 时间差

计算科学 算法 排序
2021-11-26 18:25:59

我正在测试基于 Niklaus Wirth 算法的快速排序。我正在检查需要多少时间对数组进行排序。有趣的是,对于由这些序列中的元素组成的数组:

1 3 5 7 9 10 8 6 4 2 (示例数组在更大实例中的样子)

0.14 秒 - 包含 99999 个元素的数组

5.91 秒 - 包含 100000 个元素的数组

0.11 秒 - 包含 100001 个元素的数组

对于 100000 个元素,它可以持续很长时间。你知道为什么吗?

我正在使用 Windows。在 C++ 中使用快速排序的代码:

    void quicksort (int l,int p)
    {
        int i, j, x, w;
        i=l;
        j=p;
        x=tab[(l+p)/2];

    do
        {
            while (tab[i]<x) i=i+1;
            while (x<tab[j]) j=j-1;

            if (i<=j)
            {
                w=tab[i];
                tab[i]=tab[j];
                tab[j]=w;
                i=i+1;
                j=j-1;
            }

        }while(!(i>j));

        if(l<j)quicksort(l,j);
        if(i<p)quicksort(i,p);
    }
2个回答

如果快速排序始终选择导致分区不均匀的枢轴,则它会表现出次优的时间性能。

平时速度O(nlogn)在时间上取决于在每次通过时将输入分成两个相等(或至少几乎相等)的部分。如果你经常得到一个高度不平衡的除法,那么你会得到一个非常不同的时间行为。在最坏的情况下——每次遍历只删除一个元素——快速排序最终会出现时间行为O(n2). 当您选择最后一个元素中的第一个作为枢轴并对输入进行排序时,就会出现这种情况。

您的枢轴选择——从中间开始——需要稍微复杂一点的输入来获得病态行为,但取决于输入的细节和确切的长度(因为你选择了“中间”元素),你可以有一个或更多“坏”传球。看起来你在那个长度上得到了很多糟糕的传球。

通常认为随机选择枢轴元素足以将病理行为的几率降低到令人满意的水平。

或者,还有其他算法(如合并排序),其划分不依赖于从没有故障模式的输入中选择一个值。

我猜您会看到大量由于地址冲突而导致的非强制缓存未命中,但如果不实际测量缓存未命中的数量,就很难验证这一点。也就是说,50 倍是一个相当大的性能差异。如果您调整枢轴策略,这种大的不一致应该会消失(正如 JM 在评论中建议的那样,您确实应该使用随机枢轴)。