【发布时间】:2017-01-22 23:28:54
【问题描述】:
我有一个带有重复值的std::vector<int>。我可以使用std::unique() 和std::vector::erase() 找到唯一值,但是如何通过逆映射向量有效地找到索引向量并在给定唯一值向量的情况下构造原始向量。请允许我用一个例子来说明这一点:
std::vector<int> vec = {3, 2, 3, 3, 6, 5, 5, 6, 2, 6};
std::vector<int> uvec = {3, 2, 6, 5}; // vector of unique values
std::vector<int> idx_vec = {0, 1, 4, 5}; // vector of indices
std::vector<int> inv_vec = {0, 1, 0, 0, 2, 3, 3, 2, 1, 2}; // inverse mapping
逆映射向量是这样的,它的索引可以使用唯一向量构造原始向量,即
std::vector<int> orig_vec(ivec.size()); // construct the original vector
std::for_each(ivec.begin(), ivec.end(),
[&uvec,&inv_vec,&orig_vec](int idx) {orig_vec[idx] = uvec[inv_vec[idx]];});
并且索引向量只是原始向量中第一次出现唯一值的向量索引。
我的基本解决方案远非有效。它不使用 STL 算法,最坏的情况是 O(n^2)。
template <typename T>
inline std::tuple<std::vector<T>,std::vector<int>,vector<int>>
unique_idx_inv(const std::vector<T> &a) {
auto size_a = size(a);
std::vector<T> uniques;
std::vector<int> idx; // vector of indices
vector<int> inv(size_a); // vector of inverse mapping
for (auto i=0; i<size_a; ++i) {
auto counter = 0;
for (auto j=0; j<uniques.size(); ++j) {
if (uniques[j]==a[i]) {
counter +=1;
break;
}
}
if (counter==0) {
uniques.push_back(a[i]);
idx.push_back(i);
}
}
for (auto i=0; i<size_a; ++i) {
for (auto j=0; j<uniques.size(); ++j) {
if (uniques[j]==a[i]) {
inv[i] = j;
break;
}
}
}
return std::make_tuple(uniques,idx,inv);
}
将其与典型的 std::sort+std::erase+std::unique 方法(顺便说一下,它只计算唯一值而不是索引或逆)进行比较,我在笔记本电脑上使用g++ -O3 得到以下时序[对于size=10000 的向量,只有一个重复值]
Find uniques+indices+inverse: 145ms
Find only uniques using STL's sort+erase+unique 0.48ms
当然,这两种方法并不完全相同,因为后者对索引进行排序,但我仍然相信我在上面发布的解决方案可以大大优化。有什么想法我怎么能做到这一点?
【问题讨论】:
标签: c++ performance c++11 stl