【问题标题】:map triple of unsigned int's to double -- is it the most optimal/efficient way?将无符号整数的三倍映射为双倍——这是最优化/最有效的方式吗?
【发布时间】:2012-06-11 13:21:15
【问题描述】:

我面临以下任务: 将无符号整数的三元组映射到一个值,该值为零或正数(双精度);并且能够为给定的三元组获得双倍或说它实际上为零;

我有以下简化: 第一个(我们称之为 I)和第二个(我们称之为 J)整数在已知范围内(从零到 IMAX 和 JMAX)。 对于第三个索引 (K),我知道三元组的估计值(第三个索引是分散的),比如 ESTIMATE; 在计算三元组增长的数量时,更准确地说,对于给定的 I 和 K,第三个指数的数量可以增加。

到目前为止,我看到了以下解决方案:

保持地图向量的向量:

vector< vector < map <unsinged int, unsigned int>>>  tri_map_;
//^I    ^J            ^K            ^index

如果'index'不为零,则从补充向量中获取值:

vector< double> values;
values[index];

整个东西被初始化为:

tri_map_.resize(IMAX);
for (int i=0;i<IMAX;++i) tri_map_[i].resize(JMAX);

您如何看待该解决方案?有没有更好的方法?

我不喜欢的是,我似乎不能为 地图。有没有办法尝试分配足够的内存(因为我估计了第三个索引)并检查是否有足够的内存?除此之外我很满意。

编辑1: IMAX ~ 数百 JMAX ~ 10^5

编辑2: 试图合并 sehe 的解决方案,但是对于 unordered_set 和对; 所以,这就是我遇到问题的地方specialization of ‘template&lt;class _Tp&gt; struct std::tr1::hash’ in different namespace: ... EDIT3:以下作品,将调查它的速度。 谢谢大家的建议和建议!

#include <tr1/functional>
#include <vector>
#include <map>
#include <tr1/unordered_set>
#include <list>
#include <set>
#include <tr1/array>
#include <iostream>

struct pair_int {
    unsigned int first;
    unsigned int second;

    bool operator< (pair_int const& o) const {
        if ( first < o.first )
        return true;
        if ( first > o.first )
          return false;
        return second < o.second;
   }
   bool operator==(pair_int const& o) const {
        return ( (first==o.first)&&(second==o.second) );
   }
};

namespace std {
namespace tr1 {
    template<> struct hash<pair_int> {
        unsigned int operator()(pair_int const& key) const {
            return  ~key.first + 17u*key.second;
        }
    };
}
}

class pair_storage {
public:
    pair_storage() {};  
    ~pair_storage();
    ....
private:
    pair_int pair_ij_;
    std::map<pair_int,double>::iterator  pairMapIterator_;
    std::vector< std::map<pair_int,double> >  pairMapVector_;
    std::vector< std::tr1::unordered_set< pair_int > > pairMapVectorZero_;
};

无法使用-std=c++0x 编译它,因为我在大代码的某些部分有一些问题...

【问题讨论】:

  • unsigned int 的三元组通常有 93 个有效位,而非负的double 只有 63 位。因此,即使您最好的地图也将是多值的。这是想要的吗?
  • 由于 IMAX 和 JMAX 是事先已知的,请考虑使用具有派生索引的单个向量(例如 idx = i * JMAX + j)
  • 你的地图有什么要求?满足您所说的最简单的方法是将所有三元组映射为零。
  • @Walter 抱歉,我不明白你所说的“即使你最好的地图也是多值的”是什么意思
  • @alexm 感谢您的建议。完全同意

标签: c++ optimization map


【解决方案1】:

在我看来你正在寻找

这是一个使用 std::unordered_multimap 的简单示例(这需要将 std::hash&lt;&gt; 特化为您的密钥类型,因此编写它的方式稍微复杂一些):

#include <tuple>
#include <unordered_map>
#include <cassert>
#include <iostream>

struct triplet 
{ 
    unsigned a,b,c;
    bool operator< (triplet const& o) const { return std::tie(a,b,c) < std::tie(o.a,o.b,o.c); }
    bool operator==(triplet const& o) const { return std::tie(a,b,c) ==std::tie(o.a,o.b,o.c); }
};

namespace std {
    template<> struct hash<triplet> {
        unsigned int operator()(triplet const& key) const { 
            return  ~key.a + 17u*key.b + 17u*key.c; // totally made that up, could be better, I suppose
        }
    };
}

static std::ostream& operator<<(std::ostream& os, triplet const& key) {
    return os << '[' << key.a << ',' << key.b << ',' << key.c << ']';
}

int main()
{
    std::unordered_multimap<triplet, double> map;

    // insert items dynamically
    map.insert({ triplet{ /*I*/ 1, /*J*/ 2, /*K*/ 3 },  0.1 } );
    map.insert({ triplet{ /*I*/ 4, /*J*/ 5, /*K*/ 6 },  0.2 } );
    map.insert({ triplet{ /*I*/ 7, /*J*/ 8, /*K*/ 9 },  0.3 } );
    map.insert({ triplet{ /*I*/ 1, /*J*/ 2, /*K*/ 0 },  0.4 } ); // duplicate (I,J) ok

    map.insert({ triplet{ /*I*/ 1, /*J*/ 2, /*K*/ 0 }, 0.5 } );

    assert(0 == map.count(triplet {1,5,6}));
    assert(1 == map.count(triplet {4,5,6}));

    auto range = map.equal_range(triplet { 1,2,0 });
    for (auto it=range.first; it!=range.second; ++it)
        std::cout << it->first << ": " << it->second << "\n";
}

输出(见 http://ideone.com/pm8Oz):

[1,2,0]: 0.4
[1,2,0]: 0.5

【讨论】:

  • 非常感谢您提供的详细示例。您认为 unordered_map 在插入和搜索方面是否比仅映射更快?
  • @Denis 取决于使用模式。我认为基于树的(“有序”)在小尺寸下可能更快,尤其是当条目需要按升序遍历时(这是std::(multi_)map 的“副作用”)。从本质上讲,您只需配置文件来衡量差异。
  • 我不认为您可以将std::hash 专门用于标准类型(在本例中为triplet)。即使你被允许,也要考虑“如果每个人都做了呢?”的原则。答案是违反 ODR,因为它们不会都使用相同的哈希函数。相反,您可以将自己的类型定义为 tuple&lt;unsigned,unsigned,unsigned&gt; 的包装器并将其用作密钥,或者您可以将自己的哈希器提供给您的 unordered_multimap,而不是让它使用默认的 std::hash&lt;KeyType&gt;
  • @SteveJessop 哎呀。是的你是对的。您可以专门化 std::hash,但仅限于用户定义的类型。实际上,我认为 std::hash> 是标准中规定的,但显然不是。这是 std::tuple 的一个可用性问题。 编辑固定
  • @sehe:我当然同意这是一个可用性问题。尤其是因为我经常使用 Python,并且通常使用元组作为字典键,所以我觉得 C++11 改变了我。也许出于某种原因,委员会不想就如何组合散列提出要求?
【解决方案2】:
struct Triple
{
    unsigned int A;
    unsigned int B;
    unsigned int C;
};

map<Triple, double> mapping;

【讨论】:

  • 我不明白为什么这会比 vector 更快,消耗更少的内存; + 向量 非零
  • @Denis - 我不明白为什么它不会更快和/或消耗更多内存。不管怎样,我都看不到,因为——我做不到!我还没有测量任何东西。 Peter 的代码最清楚地表达了意图是什么,为此,他得到了 +1。
  • @HappyGreenKidNaps 我想人们从 O(logN) 就知道了。如果我在一张地图中加入所有三元组,则搜索为 O(log IMAXJMAX...),但如果我为每对 I-J 保留“个人”地图,则搜索 O (log ...),它肯定比上面那个小。我会说在这种情况下不需要测试。
  • @HappyGreenKidNaps 我意识到的另一点是,在这种方法中,我需要存储很多零。在我看来,map 和 unordered_set 两个向量的组合更有效。
【解决方案3】:

关于“这是最优化/最有效的方式吗?”的问题:

尝试使用hash_mapunordered_map;这可能比map 更快(或者可能不会,这取决于您的用例)。

关于“有没有办法尝试分配足够的内存......?”的问题:

您可以使用unordered_map::max_load_factor 来调整内存/性能权衡;这就像预分配一样。

哦,您还可以使用unordered_set 来存储映射为零的元素。这可能会减少内存消耗而无需性能成本。

【讨论】:

  • 感谢您的回复。如果我现在只新哪一个更快? :) 关于 unordered_set,这确实会节省大量内存。谢谢你的想法。然后,我实际上不需要任何索引向量,但我需要进行搜索,一个在 unordered_set 中,一个在 K -> double 的某种映射中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
相关资源
最近更新 更多