【问题标题】:Find the indices and inverse mapping of a unique vector查找唯一向量的索引和逆映射
【发布时间】: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


    【解决方案1】:

    如果我没记错的话,下面的解应该是O(n log(n))

    (我已经更改了std::size_t 值中的索引)

    template <typename T> 
    inline std::tuple<std::vector<T>,
                      std::vector<std::size_t>,
                      std::vector<std::size_t>>
    unique_idx_inv(const std::vector<T> &a)
     {
       std::size_t               ind;
       std::map<T, std::size_t>  m;
       std::vector<T>            uniques;
       std::vector<std::size_t>  idx;
       std::vector<std::size_t>  inv;
    
       inv.reserve(a.size());
    
       ind = 0U;
    
       for ( std::size_t i = 0U ; i < a.size() ; ++i )
        {
          auto e = m.insert(std::make_pair(a[i], ind));
    
          if ( e.second )
           {
             uniques.push_back(a[i]);
             idx.push_back(i);
             ++ind;
           }
    
          inv.push_back(e.first->second);
        }
    
        return std::make_tuple(uniques,idx,inv);
    }
    

    【讨论】:

      【解决方案2】:

      O(n^2) 源于您使用向量上的嵌套循环识别重复项的方法。但是,要确定一个元素是否已被读取,排序向量或 - 恕我直言更好 - 无序映射更合适。 所以,不用在这里写代码,我建议使用表单的无序映射

      unordered_map&lt;int,int&gt;,可以同时保存唯一值和索引。我不确定您是否仍需要这些信息的向量,但您可以轻松地从地图中导出这些向量。

      复杂度应该降低到O(n log(n))

      【讨论】:

        猜你喜欢
        • 2014-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-10
        • 1970-01-01
        • 1970-01-01
        • 2018-03-14
        相关资源
        最近更新 更多