【问题标题】:How to aggregate values in C++ using keys?如何使用键在 C++ 中聚合值?
【发布时间】:2012-09-27 14:43:16
【问题描述】:

在 C++ 中,如何根据三个键为结构聚合值?

在 Perl 中,我会使用哈希值(例如 $hash{$key1}{$key2}{$key3}{'call_duration'} += 25);

由于我是 C++ 的新手,您能否建议一种合适的方法?

我已经查看了关于 SO 在 C++ 中使用 std::map 讨论嵌套哈希等效项的主题,但是它指出这在性能方面很慢,并且由于我需要为电信运营商处理记录,因此性能至关重要.

我没有必要遵循使用模板库或任何在语法和思维方式上应该类似于 Perl 的方法,但是如果您不得不做类似的事情,您能否分享一个快速且合适的方法来实现它?

我主要受限于 C++ 98 标准(技术主管允许使用更新的功能,前提是它们得到编译器的支持并且具有关键优势)。

如果描述混乱,请见谅,提前致谢!

edit:编译器版本是 GCC 4.1.2,将 tr1/functional 作为库导入并不被它所反对。

edit:非常感谢所有加入的人,特别是 Bartek 和 Rost 忍受了我的愚蠢问题。我决定选择 Rost 的答案,因为这是我真正能够开始工作的! :)

【问题讨论】:

  • 以三元组为键的地图听起来很合理。
  • 你能澄清你想要做什么吗?这也有助于了解密钥的类型。 (如果要使用搜索引擎,请搜索“无序地图”。)
  • std::map<std::tuple<>, TValue>std::map<TStructOfThreeKeys, TValue>boost::multi_index
  • 你可以使用 TR1 库吗?
  • 如果您正在寻找 C++ 中的哈希图,请查看此处:en.cppreference.com/w/cpp/container/unordered_map 如果您使用的是支持 C++11 的编译器。

标签: c++ hash aggregation


【解决方案1】:

常见的std::map 应该是合适的,它的性能在大多数情况下通常不是问题。哈希提供对元素的恒定时间访问,基于树的映射提供对数时间,但实际上恒定时间可能大于对数 - 这取决于具体实现和具体数据。如果您填充一次容器然后只更新数据而不更改/插入/删除键,您可以使用排序的std::vectorLoki::AssocVector

您应首先尝试std::map(或std::set,如果密钥实际上是数据的一部分),然后才能决定它对您来说是否太慢。示例:

// Composite key definition
struct CompositeKey
{
   int key1;
   std::string key2;
   AnotherType key3;

   CompositeKey(int i_key1, const std::string& i_key2, AnotherType i_key3):
      key1(i_key1), key2(i_key2), key3(i_key3)
   {}

   bool operator < (const CompositeKey& i_rhs) const
   {
      // You must define your own less operator for ordering keys
   }
};

// Usage
std::map<CompositeKey, Data> aggrData;

aggrData[CompositeKey(0, "KeyString", AnotherType())] = Data();

if(aggrData.find(CompositeKey(0, "KeyString", AnotherType())) != aggrData.end())
{
   // Process found data
}

对于进一步的性能研究,您可以尝试:

所有这些容器都有类似的接口,因此封装起来并不困难,如果需要的话可以轻松切换实现。

【讨论】:

  • 没有std::hash_map这样的东西。
  • @DeadMG 你说得对,已修复。我记得它在早期的 GCC 版本中是 std
  • 对不起 Rost,你能帮我定义 Data() 吗?我创建了一个名为“Data”的结构,它以与 CompositeKey 类似的方式初始化,但是每当我将它作为 Data(3, 3.4f) 传递时,编译器都会抱怨没有匹配的函数用于调用 Data::Data()。我在 Key 中添加了一个比较运算符,在 Data 中添加了一个构造函数,所以不确定问题是什么:( 谢谢。
  • @Nobilis 看起来你错过了 Data 的默认 ctor(没有参数或所有参数都具有默认值),它需要使用 map::operator[]
  • @Nobilis Aaah,这就是问题所在。您刚刚声明了Data(),但没有定义它。将 Data(); 更改为 Data(): count(0), weight(0) {} 即可。
【解决方案2】:

简单的解决方案是使用 struct 聚合 3 个键,并将其用作键。

struct Key
{
    Type1 Key1;
    Type2 Key2;
    Type3 Key3;

    // I forgot about the comparator - you have to provide it explicitly
};

由于您的语言有些受限,请检查您的编译器是否支持std::hash_map

std::hash_map<Key, TValue> Data;

如果没有,您可以随时使用boost::unordered_map

如果其他人偶然发现了同样的问题,那么“正确的解决方案”是这样的:

std::unordered_map<std::tuple<Type1, Type2, Type3>, TValue>;

编辑:示例用法

struct Key
{
    int Int;
    float Float;
    string String;
    // add ctor and operator<
};

std::hash_map<Key, int> Data;

Data[Key(5, 3.5f, "x")] = 10;

【讨论】:

  • boost::multi_index 在容器上提供多个视图,而不是多个维度或哈希表(或者我可能误解了您的答案)。
  • 是的,经过一番思考,您的评论是有道理的。该对象实际上有 one 键 - 由 3 个子键组成。所以它不完全是 multi-index
  • @BartekBanachewicz 谢谢你的回答,我确实有这样的想法,但这是否意味着性能会变慢?正如我想象的那样,如果你将它们嵌套,一旦你找到你的第一个键,你只需要在那个键中搜索等等。如果我正在搜索整个字符串 (key1+key2+key3) 并不意味着在最坏的情况我将不得不遍历整个列表?由于传入的记录不会被排序,因此将它们按顺序排列可能会导致一些性能损失。如果我误解了你,请纠正我。
  • @Nobilis - std::(hash_)map 在其内部表示中使用树,因此搜索在 O(log n) 中完成。此外,如果键是散列的,则每个查询都会对传入的键进行散列,然后将其与现有的键进行比较。键是否具有 3、5 或 1000 个元素并不重要。所以一旦你找到你的第一个键,你只需要在那个键中搜索等等 - 是错误的。正如我之前写的,实际上只有 一个 键。
  • @Nobilis 在大多数情况下,散列容器不执行键值搜索。他们通过散列函数计算项目位置。它需要项目键并返回容器中的项目位置。所以它的时间是恒定的,可以直接访问项目而无需任何迭代。当存在 collisions 时会出现问题 - 当哈希函数为不同的键生成相同的位置时。所以在最坏的情况是的,它可能是 O(n)(“遍历整个列表”)。基于树的地图总是具有 O(log n) 复杂度,并且永远不会遍历整个树。
猜你喜欢
  • 2013-06-06
  • 2015-02-24
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
相关资源
最近更新 更多