三方快速排序需要更高的性能

Need higher performance for three-way quick sort

本文关键字:性能 快速排序 三方      更新时间:2023-10-16

我目前正在尝试实现一个三分区快速排序。下面的代码运行良好,但运行时间不够。我对数据结构、算法和"深度"编程都是新手,所以我试图在更短的时间内摆弄它,但基本上没有成功。(内存性能良好。)

我的直觉是改变支点,但我担心这不会是一个三方快速排序。

#include <iostream>
#include <vector>
#include <cstdlib>
using std::vector;
using std::swap;

int partition3(vector<int> &a, int l, int r) {
    int x = a[l]; 
    int j = l;
    int k = r;
    int i = l+1; 
    while (i <= k) {
        if (a[i] < x) {
          swap(a[i],a[j]);
          j++; 
          i++;
        }
        else if(a[i] > x) {
            swap(a[i],a[k]);
            k--;
        }
        else {
            i++;
        }
    }
    return j;
}
void randomized_quick_sort(vector<int> &a, int l, int r) {
    if (l >= r) {
        return;
    }
    int k = l + rand() % (r - l + 1);
    swap(a[l], a[k]);
    while (l < r) {
        int m = partition3(a, l, r);
        if ((m-l) < (r-m)) {
            randomized_quick_sort(a, l, m - 1);
            l = m + 1; 
        }
        else {
            randomized_quick_sort(a, m + 1, r);
            r = m - 1;
        }
    }
}
int main() {
    int n; 
    std::cin >> n;
    vector<int> a(n);
    for (size_t i = 0; i < a.size(); ++i) {
        std::cin >> a[i];
    }
    randomized_quick_sort(a, 0, a.size() - 1);
    for (size_t i = 0; i < a.size(); ++i) {
        std::cout << a[i] << ' ';
    }
}

排序在现实世界中是一个相当复杂的问题。试着看看一些高效的实现,例如那些由C++标准库的实现提供的实现。浏览网页,阅读文章,查看讨论。。。

只是一些注意事项:

  1. 随机数生成是(相对)昂贵的,它可以显著降低快速排序的速度。(然而,对于某些类型的数据,它也可以起到相反的作用。)
  2. 整数除法(相对而言)非常昂贵,可能比随机数生成更昂贵
  3. 在实践中很少单独使用纯快速排序。通常,它与插入排序相结合,因为递归调用对于非常小的分区效率很低(阈值通常设置在8到16个元素之间)
  4. 为了防止最坏情况下的快速排序复杂性,通常会检查递归级别,如果它超过某个阈值(2 x log_2(n)),则用另一种算法(通常为堆排序)对其余数据进行排序

等等。。。

更新

还有两个想法:

  1. 在多核/多核环境中,并行算法可能会为您提供最佳的加速。但是,设计一个并行的快速排序绝非易事。大部分复杂性都落在高效的并行分区和负载平衡上。Libstdc++并行模式有一个很好的OpenMP实现。或者,您也可以查看我的AQsort:https://github.com/DanielLangr/AQsort.
  2. 要提高快速排序的效率,请使用尾部调用消除/优化。它大大减少了所需的调用堆栈空间

我在这里看到了非常扎实的代码。

如果你想保留算法,最好的加速方法是将其从递归改为迭代。这不会是一个巨大的推动,但会有所帮助,每个功能调用都是一个很好避免的开销。手动交换也是一个不错的选择。

您还可以获得一定的速度来避免额外的内存分配,所以您应该尽可能多地重用变量,例如,在while中声明int m。