面对昂贵掉期的双枢轴快速排序

Dual pivot quicksort in face of expensive swaps

本文关键字:快速排序 面对      更新时间:2023-10-16

把这个问题转给了程序员,因为它对CS来说似乎不够理论。
TLDR
有没有人测试过使用昂贵的交换元素的双枢轴快速排序性能?在这种情况下,它的性能应该大大低于标准快速排序。


基本信息
受到最近关于堆栈溢出的"问题"的启发,我决定去实现给定排序的非平凡版本(内流排序,3向分区快速排序,3个支点选择的中位数,小块插入排序等)。

在一些研究过程中,我还发现了双枢轴快速排序,这是Java标准库中快速排序的当前实现。一般来说,它声称它总是至少和标准快速排序一样好,经验测试似乎支持它。(这就是它是当前实现的原因。)

然而,似乎没有STL实现在内敛排序的快速排序阶段使用双枢轴快速排序,这让我想知道为什么。经过更多的研究,我找到了这篇论文。它表示,虽然双枢轴快速排序执行的比较平均减少了5%,但它执行的交换却明显更多。(大约增加80%)显然,由于Java只有原语和引用类型,交换总是很便宜。(即便如此,它也只对原语使用这种排序,因为它不稳定)

所以我想看看是否有人已经测试过标准快速排序与双轴快速排序时,元素是昂贵的交换和有数字(可能源)躺在周围,或者我是否必须自己测试。

这个问题是关于快速排序变量的

我在论文中已经对此进行了广泛的研究。https://arxiv.org/ftp/arxiv/papers/1505/1505.00558.pdf

简短的回答是否定的。当交换大元素时,与高端版本的快速排序相比,Dual Pivot的性能并不好。请看图22和图23。

我认为论文的QUICKSORTYAROSLAVSKIY()不够快有3个原因。

  1. 很难找到一个好的支点。通常两个支点太近或太远。
  2. swap太多。例如,第12行中的交换是部分数组滑动。
  3. 插入排序实现是不实际的。论文中的Knuth算法可能是用来教学的。我不喜欢部分数组滑动。

QUICKSORTYAROSLAVSKIY()的优点是2个枢轴被排除在分区之外。

我设计了一种算法使快速排序更快。因为交换是一种冗余的方法,当N较大时,需要在各个元素中选择一个枢轴。更多. .

如果您的目标是减少交换的次数,您应该退回到排序指针。像这样:

void sort(vector<BigClass> &data) {
    //First get an array of pointers.
    vector<BigClass*> temp;
    temp.reserve(data.size());
    for(BigClass& current : data) temp.push_back(&current);
    //Sort.
    sort(temp.begin(), temp.end(), [](const BigClass* &a, const BigClass* &b){
        /* some lambda to compare the pointers by the objects they point to */
    });
    //Move the objects.
    vector<BigClass> result;
    result.reserve(data.size());
    for(BigClass* current : temp) result.push_back(*current);
    //Update the argument
    swap(result, data);
}

保证精确地执行data.size()复制构造。你做得再好不过了。