【问题标题】:Sorting one vector with respect to another - most efficient way?对一个向量相对于另一个向量进行排序——最有效的方法?
【发布时间】:2026-01-11 08:45:01
【问题描述】:

我知道这个问题has alreadybeen askeda fewtimes,但是对于简单的情况(其中紧凑性、可读性或用户熟练度是决定因素)提供了不同的答案,我不是确定哪一个是最有效的,因为我担心重复该操作 O(1M) 次

设置如下:

  • float的两个向量AB;这无法更改,但可以从 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}

问题:

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

【问题讨论】:

  • “快速 + 节省内存”往往是相互排斥的。你需要选择你真正关心的。最重要的是 - 你尝试过什么,为什么你认为它还不够好?
  • 它必须是两个向量吗,或者你可以有一个带有A和B字段的结构向量和
  • 因为向量的长度为 4 且最多为 20,所以我认为处理您的特别小的情况的手工编码算法将是获得您想要的东西的最佳方式。
  • 在不重新编码排序算法的情况下执行此操作的一种有趣方法可能是使用某种 zip_iterator 并使用比较函子 (x,y) RandomAccessIterator 的确切要求(主要是因为 iterator_traits::reference 不是真正的引用,这是 ForwardIterator 所要求的,这是由 RandomAccessIterator 暗示的)。但我想你还是可以试试 boost 的 zip_iterator。

标签: c++ performance sorting vector


【解决方案1】:

AB 的长度相等,至少为 4,最多为 20(如果这对您有帮助的话)。

由于它们的大小相同,您可以将指向 B 值的指针存储在 A 中,从而消除根据 A 重新排列 B 所需的 O(n) 时间.每次你想做某事时,你想使用的方法都会让你付出代价。到AB

[...]哪个效率最高,因为我担心重复该操作 O(1M) 次。

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

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

我会推荐Block Sort 来解决这类问题。这是一个稳定的 O(nlogn) 时间复杂度,当然还有 O(1) 内存使用。

在这里,您可以在 CC++ 中实现它,命名为:Wiki Sort。与 std::stable_sort() 分析不同数据排序的算法行为也有很好的比较。

【讨论】:

    【解决方案2】:

    一种常见的方法是创建索引并对其进行排序,而不是对原始值进行排序。这称为间接排序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}
    

    【讨论】:

      【解决方案3】:

      在这种情况下,用std::sort 击败std::pair&lt;float, float&gt; 真的很难,这来自一个尝试过很多次的人:

      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 百万单精度浮点数.一旦你开始谈论你正在谈论的 4-20 个元素的输入大小,就很难击败std::sort。我已经用最微调的插入排序尝试了一整天以极小的输入尺寸击败std::sort(对于大输入尺寸而言,击败std::sort 非常容易,这让我一遍又一遍地尝试在我改进我的组装时,每年或每两个周末也尝试在小输入上击败它和计算机体系结构知识,但至少在我看来是不可能的,因为我的技能/缺乏能力以微不足道的输入击败它)。我还筛选了用于对数字进行排序的各种库,对于小输入,它们也不会击败std::sort,对于大输入,我也不会(如果可以的话,我不会费心为大输入手动处理我自己的数字排序)只需从其他地方插入一个)。

      这些其他建议,如间接/算法排序对于非平凡的输入大小往往非常出色,但对于微不足道的输入大小来说,击败std::sort 真的很困难(如果你愿意,4-24 32 位元素真的很微不足道)问我)。可能是最微调的插入排序或堆排序或某种其他类型的二次复杂度 (O(N^2)) 排序作为您最好的选择,可能使用某种超级花哨的 SIMD 实现或类似的东西。我们不应该考虑这些小规模的算法复杂性:主要是机器指令,考虑如何并行化排序并一次排序多个小序列而不是试图让每个单独的排序可能更有效率对于如此微小的输入,速度会更快。

      我一直对更快的浮点数类型很感兴趣,因为它们可以缩短用于光线跟踪和其他领域的某些 Kd 树和 BVH 的构建时间,这可以为工作室(如 Pixar 和 ILM pour 之类的工作室)节省大量资金只是在他们的渲染农场上花费了很多钱),但我从来没有能够在输入大小上击败std::sort,比如少于 64 个浮点数(

      也就是说,节省内存的部分很容易。就地排序(std::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。

      【讨论】:

        最近更新 更多