![](/img/trans.png)
[英]How to write a hash function for a std::vector<std::vector<bool>>
[英]Fast hash function for `std::vector`
我实现了这个解决方案,用于从vector<T>
获取哈希值:
namespace std
{
template<typename T>
struct hash<vector<T>>
{
typedef vector<T> argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& in) const
{
size_t size = in.size();
size_t seed = 0;
for (size_t i = 0; i < size; i++)
//Combine the hash of the current vector with the hashes of the previous ones
hash_combine(seed, in[i]);
return seed;
}
};
}
//using boost::hash_combine
template <class T>
inline void hash_combine(std::size_t& seed, T const& v)
{
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
但是这个解决方案根本没有扩展:使用1000万个元素的vector<double>
,它将花费超过2.5秒(根据VS)。
是否存在针对此场景的快速哈希函数?
请注意,从向量引用创建哈希值不是一个可行的解决方案,因为相关的unordred_map
将在不同的运行中使用,此外,两个具有相同内容但不同地址的vector<double>
将以不同方式映射(此行为的不良行为)应用)。
MSVC旧的hashmap使用的方法是不经常采样。
这意味着隔离的更改不会显示在您的哈希中,但您要避免的是读取和处理整个80 MB的数据以便对您的向量进行哈希处理。 不读一些字是非常不可避免的。
你应该做的第二件事是不在所有vector
上专门化std::hash
,这可能会使你的程序格式不正确(正如我不记得的缺陷解决方案所示),至少是一个糟糕的计划(因为std
肯定允许自己添加散列组合和散列矢量)。
当我编写自定义哈希时,我通常使用ADL(Koenig Lookup)来扩展它。
namespace my_utils {
namespace hash_impl {
namespace details {
namespace adl {
template<class T>
std::size_t hash(T const& t) {
return std::hash<T>{}(t);
}
}
template<class T>
std::size_t hasher(T const& t) {
using adl::hash;
return hash(t);
}
}
struct hash_tag {};
template<class T>
std::size_t hash(hash_tag, T const& t) {
return details::hasher(t);
}
template<class T>
std::size_t hash_combine(hash_tag, std::size_t seed, T const& t) {
seed ^= hash(t) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template<class Container>
std::size_t fash_hash_random_container(hash_tag, Container const& c ) {
std::size_t size = c.size();
std::size_t stride = 1 + size/10;
std::size_t r = hash(hash_tag{}, size);
for(std::size_t i = 0; i < size; i += stride) {
r = hash_combine(hash_tag{}, r, c.data()[i])
}
return r;
}
// std specializations go here:
template<class T, class A>
std::size_t hash(hash_tag, std::vector<T,A> const& v) {
return fash_hash_random_container(hash_tag{}, v);
}
template<class T, std::size_t N>
std::size_t hash(hash_tag, std::array<T,N> const& a) {
return fash_hash_random_container(hash_tag{}, a);
}
// etc
}
struct my_hasher {
template<class T>
std::size_t operator()(T const& t)const {
return hash_impl::hash(hash_impl::hash_tag{}, t);
}
};
}
现在my_hasher
是一个普遍的哈希。 它使用声明为哈希my_utils::hash_impl
(对于std
型),或免费的功能叫做hash
将哈希给定类型,刨根问底。 如果失败了,它会尝试使用std::hash<T>
。 如果失败,则会出现编译时错误。
在你想要哈希的类型的命名空间中编写一个免费的hash
函数往往比在我的经验中不得不关闭和打开std
并专门化std::hash
更烦人。
它以递归方式理解向量和数组。 做元组和对需要更多的工作。
它对所述载体和阵列进行约10次采样。
(注: hash_tag
是既带有一点玩笑的,并且这种方式强制ADL和防止不得不前瞻性声明的hash
在专业化hash_impl
命名空间,因为这要求很烂。)
抽样的价格是你可能会遇到更多的冲突。
如果您拥有大量数据,另一种方法是将它们哈希一次 ,并跟踪它们何时被修改。 要执行此方法,请为您的类型使用copy-on-write monad接口,以跟踪散列是否是最新的。 现在一个矢量被哈希一次; 如果你修改它,哈希就会被丢弃。
可以进一步使用随机访问哈希(可以很容易地预测当您以哈希方式编辑给定值时会发生什么),并调解对向量的所有访问。 这很棘手。
你也可以对哈希进行多线程处理,但我猜你的代码可能是内存带宽限制的,多线程在那里也没什么用处。 值得尝试。
您可以使用比平面向量(类似于树)更高级的结构,其中值的更改以类似哈希的方式冒泡到根哈希值。 这将为所有元素访问添加lg(n)开销。 同样,您必须将原始数据包装在控件中,以使散列保持最新(或者,跟踪哪些范围是脏的并且需要更新)。
最后,因为您一次使用1000万个元素,请考虑转移到强大的大型存储解决方案,如数据库或您拥有的内容。 在地图中使用80兆字节的密钥对我来说似乎很奇怪。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.