使用 STL 排序对表进行排序

sorting table in place using stl sort

本文关键字:排序 STL 使用      更新时间:2023-10-16

我有一个巨大的表(大约50Gb(,格式为(i,j,k((来自稀疏矩阵(,存储为

uint32_t * idx1, * idx2;
float * vals;
uint32_t tablesize;

我想使用给定的比较函数对其进行排序,该函数是 idx1 和 idx2 的函数。这可以使用std::sort来完成吗?

具体来说,稀疏矩阵中每个值为 v 的非零条目 (i,j( 通过将 i 放在 idx1 中,j 放在 idx2 中,v 放在 vals 中的相应条目中来存储。然后我想根据 (i1, j1, v1( 对这些条目进行排序 <= (i2, j2, v2( 如果

(i1 < i2) || (i1==i2 && j1 <= j2)

我能够搜索到在非标准数据类型上使用std::sort的示例假设要比较的每个项目都是一个类的单个实例;在这里,每个项目都由不同数组中的三个值表示。

不幸的是,很难说服std::sort或任何标准库使用条带化数据。它旨在假设数据可以通过单个=复制,通过一个move移动或通过一个swap交换。

最好的办法是使用 boost::iterator_facade 编写一个自定义迭代器类,该类包装数据,并对std::sort隐藏条带化数据格式。我过去想做类似的事情,但我的工作空间不允许我们使用boost编辑:当您的外观被取消引用时,它可能需要创建某种可以分配/移动/交换的代理对象,并对每个条带数组执行正确的操作。这不是小事。

下一个最好的选择是制作一个从 0 到 N 的 int 数组,每个数组代表条带化数据数组中的一个索引。编写一个自定义函子来std::sort该函子对此数组进行排序以符合您的条件。当你有这么大的数据集时,这显然远非理想。

如果您必须继续使用现有的数据结构(本质上是三个std::vectorstd::tuple(,那么使用boost::zip_iterator似乎是要走的路。zip_iterator将三个迭代器(两个到索引,一个到一个值(视为单个元组,您可以使用自定义比较函数对象就地对数据进行排序。唉,boost::zip_iterator不能与std::sort一起使用,正如本问答中所解释的那样,因为它不能被写入。

这意味着您必须编写自己的 zip_iterator 类,该类可以与 std::sort 一起使用。请注意,这不是一个微不足道的练习,请参阅此问答和/或本文

std::tuplestd::vector进行排序要容易得多。我在下面的尝试使用两个索引和一个值的std::tuple,并将这些条目存储到一个std::vector中。对于排序,我使用 C++14 通用 lambda 将两个索引转发到一个较小的元组中,并使用 std::tuple 的库operator<按字典顺序比较它们(即首先在行索引上,然后在列索引上

(。
#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using index = uint32_t;
using value = float;
using sparse_entry = std::tuple<index, index, value>;
using sparse_matrix = std::vector<sparse_entry>;
int main()
{
    // sparse 3x3 matrix
    auto m = sparse_matrix { 
        std::make_tuple( 1, 1, -2.2), 
        std::make_tuple( 1, 0, 42  ), 
        std::make_tuple( 0, 2,  3.4), 
        std::make_tuple( 0, 1,  1.7) 
    };    
    // sort by row-index, then column-index
    std::sort(begin(m), end(m), [](auto const& L, auto const& R) {
        return 
            std::forward_as_tuple(std::get<0>(L), std::get<1>(L)) <
            std::forward_as_tuple(std::get<0>(R), std::get<1>(R))
        ;
    });
    for (auto const& elem : m)
        std::cout << "{ " << std::get<0>(elem) << ", " << std::get<1>(elem) << ", " << std::get<2>(elem) << "}, n";
}

活生生的例子

如果您的应用程序可以使用这种转换后的数据布局(并且可能存在缓存性能原因导致它不能(,那么上面的代码将根据需要进行排序。

注意:正如@Casey提到的,您也可以使用 std::tie 而不是 std::forward_as_tuple ,但是当您将 sparse_entry 更改为一个成熟的用户定义类并返回 getter 按值时,这可能会咬您。