【问题标题】:Getting unique elements from a container [c++]从容器中获取唯一元素 [c++]
【发布时间】:2013-05-10 19:51:51
【问题描述】:

我希望只从容器中获取唯一元素。假设srcContainer 是我想要独特元素的容器。我看了三个选项:

  1. 使用 std::unique

       std::sort(srcContainer.begin(), srcContainer.end());
       srcContainer.erase(std::unique(srcContainer.begin(), srcContainer.end()), srcContainer.end());
    
  2. 使用 BOOST::unique

    boost::erase(srcContainer, boost::unique<boost::return_found_end>(boost::sort(srcContainer)));  
    
  3. 我自己的方法

    std::set<T> uniqueElems(srcContainer.begin(), srcContainer.end());  
    srcContainer.clear();  
    srcContainer.insert(srcContainer.end(), uniqueElems.begin(), uniqueElems.end()); 
    

1. 和 2. 的问题是它们改变了成员在原始 srcContainer 中出现的顺序。与 3. 相比,顺序没有变化,此外,与上面的 1. 和 2 相比,它提供了更好的性能(是不是因为 3. ?? 中没有明确的排序)。上面 3 种方法的挂钟时间和 srcContainer 中的元素数量如下:

  1. srcContainer 的大小(包含整数)= 1e+6
    - std::unique = 1.04779 秒
    - BOOST::unique = 1.04774 秒
    - 自己的方法 = 0.481638 秒

  2. srcContainer 的大小(包含整数)= 1e+8
    - std::unique = 151.554 秒
    - BOOST::unique = 151.474 秒
    - 自己的方法 = 57.5693 秒

我的问题是:

  1. 是否有更好的方法使用 std::unique 或 BOOST::unique 或任何其他代码查找唯一性并保持容器中的原始顺序?
  2. 使用上述方法 3. 的任何问题。

对于性能分析srcContainer 创建如下:

std::vector<int> srcContainer;  
int halfWay = numElems/2;  
for (size_t k=0; k<numElems; ++k) {  
   if (k < halfWay)  
      srcContainer.push_back(k);  
   else  
      srcContainer.push_back(k - halfWay);  
}  

编辑:
同意cmets,方法3.也改变了元素的顺序。有没有更好的方法在不改变顺序的情况下获得独特的元素?

谢谢

【问题讨论】:

  • srcContainer的类型是什么?
  • 在这种情况下,我使用 srcContainer 的向量进行测试。但我希望代码适用于大多数容器类型,例如 BOOST::unique。
  • 我只是感兴趣:容器有多大?您是否分析了代码,瓶颈在哪里?你用的是什么平台?从极长的执行时间来看,我认为瓶颈在于创建来自 std::unique 而不是排序的大型容器的副本。
  • 您可以在调试模式 (g++ -g) 编译代码后使用 'valgrind --tool=kcachegrind' 分析代码,而无需开启优化。然后你可以使用 kcachegrind 找到瓶颈。
  • 谢谢。列出的运行时间仅适用于显示的部分代码。不包括创建 srcContainer 所花费的时间。上面列出了 srcContainer 的大小。

标签: c++ boost unique


【解决方案1】:

根据有关源数据的信息进行编辑: 您看到集合插入完成比对向量排序更快的原因是您的输入数据是两个已经排序的范围。对于快速排序(通常由 std::sort 使用),这是一种退化的情况,也是您可以给出的最糟糕的输入之一。对于1e8 的输入大小,将排序从std::sort 更改为std::stable_sort 将运行时间从~25s 缩短到

如果您想保留原始项目顺序,您可以尝试使用以下方法保留所有项目的哈希值。我不知道这会是什么性能,但例如,您可以使用散列和remove_if 的方法,如下所示:

struct Remover
{
    explicit Remover(hash& found_items) : found_items_(found_items) { }
    bool operator()(const Iter& item) { retval = <does exist in hash>; add to hash; return retval; }

    hash& found_items_;
};

hash dup_finder;
Remover remover(dup_finder);
std::erase(std::remove_if(src.begin(), src.end(), remover), src.end());

我的答案的原始组成部分:

如果源容器中的元素大部分已经排序,您可能会看到使用stable_sort 的性能比调用unique 之前的排序要好。如果没有有关 yoru 数据集的更多信息,我无法猜测可能导致选项 3 的性能优于 1&2 的原因。

选项 3 应该删除唯一性,但请记住,尽管您声明了什么,它仍然以与前两个选项完全相同的方式重新排列项目。

【讨论】:

  • 同意。使用 set 还会对 srcContainer 进行排序,并导致顺序丢失。有没有办法保留原订单并做得更好?
猜你喜欢
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 2018-01-17
  • 1970-01-01
  • 1970-01-01
  • 2017-06-30
  • 2015-06-06
  • 2017-04-22
相关资源
最近更新 更多