partial_sort_copy是最快的C++部分排序吗

Is partial_sort_copy the fastest C++ partial sort?

本文关键字:C++ 排序 sort copy partial      更新时间:2023-10-16

考虑以下函数median:

  real_t median(const std::initializer_list<real_t> vars) {
    real_t tmp[15];
    const unsigned x = vars.size() / 2;
    if (x & 1) {
      std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[x]);
      return tmp[x];
    }
    const unsigned y = x + 1;
    std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[y]);
    return (tmp[x] + tmp[y]) / 2;
  }

我使用部分排序来降低复杂性,因为我只需要对列表的一半进行排序。

此外,我假设std::partial_sort_copystd::partial_sortstd::nth_element快,因为在排序算法中不需要混洗(It1!=It2)。我的假设正确吗?


注意:假设real_t可以是double,所以请不要批评除法的使用。

NBB:我用的是-pedanticvars的长度不超过15个元素。

使用以下代码

#include <chrono>
#include <iostream>
#include <thread>
#include <string>
#include <array>
#include <algorithm>
volatile int answer;
const int size = 15;
std::array<std::array<int, size>, 0x100> fresh_data;
std::array<std::array<int, size>, 0x100> data;
void naive(int n) {
    auto & a = data[n];
    std::sort(a.begin(), a.end());
    answer = a[size / 2];
}
void fancy(int n) {
    auto & a = data[n];
    std::partial_sort(a.begin(), a.begin() + (size / 2 + 1), a.end());
    answer = a[size / 2 ];
}
void ghoul(int n) {
    auto & a = data[n];
    std::array<int, size / 2 + 1> temp;
    std::partial_sort_copy(a.begin(), a.end(), temp.begin(), temp.end());
    answer = temp[size / 2];
}
void nthel(int n) {
    auto & a = data[n];
    std::nth_element(a.begin(), a.begin() + size / 2, a.end());
    answer = a[size / 2];
}
void gen_data() {
    for (auto & a : fresh_data)
    for (auto & b : a)
        b = rand();
}
void regen_data() {
    data = fresh_data;
}

template <typename T>
void test(T f, std::string n) {
    regen_data();
    auto a = std::chrono::high_resolution_clock::now();
    for (auto i = 0; i < 10000; ++i)
    for (auto i = 0; i < 0x100; ++i)
        f(i);
    auto b = std::chrono::high_resolution_clock::now();
    std::cout << n << ": " << std::chrono::duration_cast<std::chrono::milliseconds>(b - a).count() << std::endl;
}
int main() {
    gen_data();
    test(naive, "             std::sort");
    test(fancy, "     std::partial_sort");
    test(ghoul, "std::partial_sort_copy");
    test(nthel, "      std::nth_element");
}

我得到以下结果:

             std::sort: 141
     std::partial_sort: 359
std::partial_sort_copy: 831
      std::nth_element: 149

在AMD Phenom II x4 2.5GHz上使用Visual Studio 2013以64位发布模式进行测试。

如果可以的话,我会选择部分快速排序。

部分快速分拣信息

但如果你只需要比较这两个。。。则部分排序比部分排序复制更好。这里你有更多关于这两种方法的信息:

部分分拣信息

部分分拣复制信息

在这里,您还可以找到Partial Quicksort的算法代码示例-它是在C和matlab中实现的:

示例-部分快速排序

您测试过代码吗?

std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[x]);不会将任何内容复制到tmp[x],因为&tmp[x]被认为是半开放范围的末尾(即,它刚刚超过最后一个有效元素)。因此,return语句访问不确定或默认构造的数组元素。

尝试以下操作:

real_t median(const std::initializer_list<real_t> vars) 
{
    real_t tmp[15];
    size_t siz = vars.size();
    if ((siz == 0) || (15 < siz)) return 0;     // or throw some sort of exception or ???
    const unsigned x = vars.size() / 2;
    std::partial_sort_copy(vars.begin(), vars.end(), &tmp[0], &tmp[x+1]);
    if (siz % 2 == 0) {
        return (tmp[x-1] + tmp[x]) / 2;
    }
    return tmp[x];
}

注意,如果给定initializer_list作为数据源,那么像nth_elementpartial_sort这样的就地修改算法将不起作用,因为初始化器列表无法修改(无论参数是否标记为constinitializer_list中的迭代器都是const限定的)。因此,必须使用标准算法函数进行复制以找到中值,方法是在调用算法之前复制列表,或者使用作为其工作一部分执行复制的算法变体,如partial_sort_copy()