我有一个vector<uint64_t> keys
和一个vector<char> vals
,大小都是N
。我想根据keys
中的条目对vals
和keys
进行排序。
一个显而易见的解决方案是复制到vector<pair<uint64_t, char>>
,对其进行排序,并将已排序的数据复制回来,但我想避免复制,我想避免对齐填充:sizeof(pair<uint64_t, char>)
由于对齐,是2*sizeof(uint64_t)
或16个字节;远远超过了所需的9个字节。
换句话说,虽然以下C ++ 11实现是正确的,但它不够有效:
#include <algorithm>
#include <tuple>
using namespace std;
void aux_sort(vector<uint64_t> & k, vector<char> & v) {
vector<pair<uint64_t, char> > kv(k.size());
for (size_t i = 0; i < k.size(); ++i) kv[i] = make_pair(k[i], v[i]);
sort(kv.begin(), kv.end());
for (size_t i = 0; i < k.size(); ++i) tie(k[i], v[i]) = kv[i];
}
虽然以下C ++ 11实现是正确的,但我想使用std::sort
而不是手工编码我自己的排序算法:
#include <algorithm>
using namespace std;
void aux_sort(vector<uint64_t> & k, vector<char> & v) {
for (size_t i = 0; i < k.size(); ++i)
for (size_t j = i; j--;)
if (k[j] > k[j + 1]) {
iter_swap(&k[j], &k[j + 1]);
iter_swap(&v[j], &v[j + 1]);
}
}
(编辑添加,以响应@kfsone)虽然以下实现是正确的,但它不是就地的,因为根据indices
的排列需要一个副本(或者,一个非常复杂的线性时间) -place permutation算法,我不打算实现):
#include <algorithm>
#include <tuple>
using namespace std;
void aux_sort(vector<uint64_t> & k, vector<char> & v) {
vector<size_t> indices(k.size());
iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(),
[&](size_t a, size_t b) { return k[a] < k[b]; });
vector<uint64_t> k2 = k;
vector<char> v2 = v;
for (size_t i = 0; i < k.size(); ++i)
tie(k[i], v[i]) = make_pair(k2[indices[i]], v2[indices[i]]);
}
将std::sort
等STL算法应用于一系列键/值对的最简单方法是什么?键和值存储在不同的向量中?
背景:我的应用程序正在读取大型(40 000 x 40 000)栅格,这些栅格代表地形,一次一行。一个栅格为每个单元格分配0到10 000 000之间的标签,使标签连续,另一个栅格为每个单元格分配0到255之间的值。我想以有效的方式对每个标签的值求和,我认为最快的方法是对标签行进行排序,对于排序期间的每个交换,在值行中应用相同的交换。我想避免手工编写std :: sort,std :: set_intersection等编码。
答案 0 :(得分:4)
范围适配器。最直接的路线是拉链范围,它分别在T和U上取两个相等的长度范围,并产生超过pair<T&,U&>
的范围。 (容器是一种范围 - 拥有其内容的范围)
然后按.first
对其进行排序(或使用默认排序,其中.second
确定关系)。
范围永远不是一个容器,成对的包装会随着zip迭代器的每个解引用而动态发生。
boost
有一个zip迭代器和zip范围,但你可以自己编写。 boost迭代器/范围may be read only,但该链接还包含一个不压缩的实现,并且升级已升级。
答案 1 :(得分:1)
您可以使用thrust库并使用sort by key功能。不是STL,但具有轻松移植到nVIdia GPU的(可疑)优势。
答案 2 :(得分:1)
事实上,很容易根据indices
就地置换输入向量(与问题中的声明相反):
#include <algorithm>
#include <tuple>
using namespace std;
void aux_sort(vector<uint64_t> & k, vector<char> & v) {
vector<size_t> indices(k.size());
iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(),
[&](size_t a, size_t b) { return k[a] < k[b]; });
for (size_t i = 0; i < k.size(); ++i)
while (indices[i] != i) {
swap(k[i], k[indices[i]]);
swap(v[i], v[indices[i]]);
swap(indices[i], indices[indices[i]]);
}
}
然而,这个解决方案可能是不可取的,因为它会导致比排序本身更多的缓存错误,因为输入是按indices
的顺序遍历的,这可能会导致每个元素出现一个缓存错误。另一方面,快速排序引起的缓存故障要少得多(O(n / B log n / M),当枢轴是随机的时,其中B是缓存行的大小,M是缓存的大小)。
答案 3 :(得分:0)
我认为不可能满足您为解决方案设置的所有限制。几乎可以肯定的是,破解STL对数组进行排序。但是,解决方案可能既笨拙又慢,而不仅仅是复制数据,对数据进行排序和复制。
如果您有此选项,则可能需要考虑将数据存储在单个vector
中。