【问题标题】:unordered_set: is pointer address a good hash?unordered_set:指针地址是一个好的散列吗?
【发布时间】:2011-12-10 11:43:24
【问题描述】:

我想在哈希集中存储一组(智能)指针,<boost/unordered_set>。经过 10 秒的思考,我想出了这个哈希函数:

typedef boost::shared_ptr<myType> ref_t;
struct SharedPtrHash : public std::unary_function<ref_t, std::size_t> {                        
    std::size_t operator()(ref_t const& obj) const {
      return reinterpret_cast<std::size_t>( obj.get() );
    }
};

我的问题是:这个哈希是个好主意吗?我认为这个哈希将有零或很少的冲突(也许引擎盖下有一些素数模数破坏了我所有的乐趣)。

更多细节:哈希的目的是回收大对象的存储,所以我需要一种快速的方法来检测一个大对象是否已经在 bin 中。

如果不是,什么是指针的理想哈希值,无论是智能指针还是哑指针?

【问题讨论】:

  • 嗯,内存地址应该是唯一的,所以我(经过 5 秒的思考)会说你很好。
  • myType 可能有一个最小对齐,可能高达 8。这意味着所有结果都可以被 8 整除。不是最大的哈希,特别是因为可能有一个模数为 2 .
  • 如果你想在你的键上使用对象身份来查找它是一个很好的哈希。如果你想要对象相等,这是一个错误。
  • 好点@Mooing Duck。有什么建议可以在这里快速散列吗?
  • @Patrick,我知道,这是为了回收大对象的存储,所以身份很重要。但是 Mooing Duck 提出了一个很好的观点,即由于对齐而具有低熵的地址

标签: c++ hash unordered-set


【解决方案1】:

如果你想检测相同的对象,即使它们的内容可能相同,你别无选择只能使用哈希中对象的地址.唯一的问题是是直接使用地址还是通过公式运行它。除以sizeof(mytype) 会收紧分布中的漏洞。

编辑:这是一个未经测试的模板实现,它应该适用于所有shared_ptr 类型,以及一个equal_to 函数来满足std::unordered_set 的要求。如果您有其他对象需要基于值而不是指针的哈希,请不要使用此通用实现。

template<typename T>
size_t hash(const std::shared_ptr<T> & ptr)
{
    return ((size_t) ptr.get()) / sizeof(T);
}

template<typename T>
bool equal_to(const std::shared_ptr<T> & left, const std::shared_ptr<T> & right)
{
    return left.get() == right.get();
}

【讨论】:

  • 哦,这比除以对齐要好。
  • 除以大小是个好主意吗?如果您混合了大小相邻的对象,则会产生大量碰撞。指针通常只会与最大成员的对齐方式(递归)对齐,不是?
  • @Kerrek,在这种情况下,哈希是特定于类型的,类似于享元,但没有值语义。因此,不会发生大小对象的混合(在同一个哈希中)
  • 我明白了——如果你只对一种具体类型执行此操作,而不是作为所有共享指针的部分特化,那么这可能确实有效。我不知道它在哈希冲突方面是否有任何区别,但它确实是有道理的。
  • @MahmoudAl-Qudsi 由于指向同一类型的所有指针都具有相同的对齐方式,这意味着所有指针的值都是相同值的乘数。例如,如果指针类型为T*T 的对齐方式为8,则只有桶 0、8、16 等...会有值。因此,您基本上只使用了可用存储桶的 1/alignment(T) 百分比,并且冲突的数量将是巨大的。
【解决方案2】:

以下代码编译完美(GCC 4.7,Boost 1.47):

#include <boost/unordered_set.hpp>
#include <boost/shared_ptr.hpp>

struct Foo { };

int main()
{
  boost::unordered_set<boost::shared_ptr<int>> s;
  boost::shared_ptr<int> pi(new int);
  s.insert(pi);

  boost::unordered_set<boost::shared_ptr<Foo>> t;
  boost::shared_ptr<Foo> pf(new Foo);
  t.insert(pf);
}

【讨论】:

  • 是的,shared_ptr默认使用上面的实现,我只是关心这个hash有多好
  • 所以....这与您在问题中提出的相同。我说的是默认情况下已经实现了。是的,会很好的。
  • 好的,您对对齐问题有何看法?地址除以sizeof(T)不是更好吗?
  • @lurscher:为 shared_ptr 编写散列的人正是想出如何制作 shared_ptr 的人。给他们一点信任。这将是一个足够好的哈希:D
  • 事实上,它对于大多数情况来说已经足够了,但是 Mark 建议的改进使它更紧凑,而且花费很少
【解决方案3】:

整数类型的默认 Boost.Hash hash 函数是恒等函数,所以我不认为对指针做同样的事情是一个坏主意。它会有相同的碰撞率。

【讨论】:

  • 对齐可能会影响您
猜你喜欢
  • 2013-09-09
  • 1970-01-01
  • 2016-08-11
  • 1970-01-01
  • 2011-03-19
  • 1970-01-01
  • 2016-11-07
  • 2019-05-10
  • 2013-01-03
相关资源
最近更新 更多