将一个向量相对于另一个向量排序 - 最有效的方法?

时间:2018-02-13 10:25:06

标签: c++ performance sorting vector

我知道这个问题has already been asked a few times,但针对简单案例提供了不同的答案(其中紧凑性,可读性或用户熟练程度是决定因素)并且我不确定哪一个是效率最高的,因为我担心重复该操作 O(1M)次

设置如下:

  • A的两个向量Bfloat;这不能更改,但可以从AB创建其他结构。
  • AB具有相同的长度,至少为4,最多为20(如果这对您有任何帮助)。
  • A需要根据其条目的值以降序进行排序,而B只需要匹配A的排序

示例:

A = {2,4,3,1} -> {4,3,2,1}
     | | | |
B = {1,2,3,4} -> {2,3,1,4}

问题:

  

最有效(快速+内存节省)的方式是什么?

3 个答案:

答案 0 :(得分:5)

一种常见的方法是创建索引并对其进行排序,而不是对原始值进行排序。这称为间接排序 argsort

示例:

using values_t = std::vector<float>;
using index_t = std::vector<uint8_t>;

index_t make_sorted_index(values_t const& values) {
    index_t index(values.size());
    std::iota(index.begin(), index.end(), 0);
    std::sort(index.begin(), index.end(), [&values](uint8_t a, uint8_t b) { return values[a] > values[b]; } );
    return index;
}

int main() {
    values_t a = {2,4,3,1};
    values_t b = {1,2,3,4};

    auto index = make_sorted_index(a);

    std::cout << "A = {";
    for(auto i : index)
        std::cout << a[i] << ',';
    std::cout << "\b}\n";

    std::cout << "B = {";
    for(auto i : index)
        std::cout << b[i] << ',';
    std::cout << "\b}\n";
}

输出:

A = {4,3,2,1}
B = {2,3,1,4}

答案 1 :(得分:3)

  

AB具有相同的长度,至少为4,最多为20(如果这对任何方式有帮助的话)。

由于你们两个都有相同的大小,你可以在[{1}}中存储指向B值的指针,消除重新排列{{1}所需的 O(n)时间根据{{​​1}}。你想用的方法每次你都想花费你的费用。到AB

  

[...]哪一个最有效率,因为我关注重复该操作 O(1M)次。

     

最有效(快速+节省内存)的方法是什么?

所以我们正在寻找用于排序~20个浮点数的线性就地算法? 艰巨的任务。

我会建议Block Sort来解决这类问题。它具有稳定的 O(nlogn)时间复杂度,当然还有 O(1)内存使用。

您可以在A&amp ;; A已命名为:Wiki Sort。对于不同的数据排序,还有一个很好的比较vs B分析算法行为。

答案 2 :(得分:1)

在这种情况下,std::pair<float, float>std::sort很难相提并论,这来自于经常尝试过的人:

Sorting 1,000,000 elements 32 times...

mt_sort: {0.220000 secs}
-- small result: [ 22 48 59 77 79 80 84 84 93 98 ]

mt_radix_sort: {0.202000 secs}
-- small result: [ 22 48 59 77 79 80 84 84 93 98 ]

std::sort: {1.779000 secs}
-- small result: [ 22 48 59 77 79 80 84 84 93 98 ]

qsort: {2.718000 secs}
-- small result: [ 22 48 59 77 79 80 84 84 93 98 ]

...并且可以轻松获得比std::sort(以及仍然需要一秒钟的tbb::sort更快的速度除了,输入大小为1 mil单精度浮子。一旦你开始谈论你正在谈论的4-20个元素的输入大小,就很难击败std::sort。我已经尝试了一整天的微调插入排序花了整整一天只有那个无尽的vtune会话只是为了最终得到相同的性能和放弃,这不是我第一次尝试为了输入大小而击败std::sort(对于大输入大小来说,它很容易被击败std::sort,这让我一遍又一遍地试图在每周或每周两次小输入时击败它我提高了我的装配和计算机体系结构知识,但对我来说似乎是不可能的,至少考虑到我的技能/缺乏能力,因为我很少投入它。我还筛选了各种各样的库来排序数字,他们也没有为小输入击打std::sort或者我的大输入(如果我输入大量输入,我不会打扰自己的数字排序)我可以从其他地方插入一个。)

对于非平凡的输入大小,像indirect / algo sort这样的其他建议往往非常优秀,但是对于微不足道的输入大小来说很难超过std::sort(并且4-24个32位元素实际上是微不足道的如果你问我)。可能是最微调的插入排序或堆排序或其他类型的二次复杂度(O(N ^ 2))排序作为您最好的选择,可能与某种超级奇特的SIMD实现或类似的东西。我们不应该考虑这些小规模的算法复杂性:大多数只是机器指令,考虑如何并行排序和排序多个青少年序列而不是尝试进行每个单独的排序可能更有效率。对于这些小小的,少年性的投入,我们会更快。

我一直对更快速的各种浮点数感兴趣,因为它们可以改善某些Kd树和光线追踪中使用的BVH的构建时间以及其他可以为工作室节省大量资金的地方(像Pixar和ILM pour这样的工作室只是在他们的渲染农场上赚了很多钱),但我从来没有能够在输入大小上击败std::sort,比如少于64个浮点数(<256字节)。同样,我很容易为数以千计的元素或更多元素击败它,但它已经非常快(以适合你的方式为内容)提供少量输入。

也就是说,节省内存的部分很容易。只需排序(std::sort将是一个开始)。不要为其他类别创建任何临时数组,例如radix sort。在这种情况下,对于如此小的输入尺寸,这也是最快的方法。

您可以使用自己的配对类型获得最微小的提升:

struct Key
{
    bool operator<(Key lhs, Key rhs) const {return lhs.a < rhs.a;}
    float a, b;
};

...在此方案中与std::pair的区别在于它无需比较b。我怀疑这会有多大帮助,因为表达式会被短路,但如果知道在比较器中没有访问b,优化器可能会更多地做一些事情。

如果你避免在这里使用std::vector来存储每个小小的序列,你肯定会获得速度提升。存储一百万个仅包含4-20个元素的向量并不高效。这将需要至少一百万个堆分配以及比大小/容量/指针容器数据所需的更多内存。而是将所有4-20百万个元素存储在一个std::vector实例中,例如,如果您需要提前收集少量序列,则对其进行排序。如果没有,请使用带有std::array的堆栈,或者仅使用上限大小为20的普通旧浮点数组。