制作哈希表

时间:2014-06-27 01:07:27

标签: hash hashtable

所以现在是我索引数据库文件格式的时候了,在查看了各种方法之后,我认为哈希表是我最好的选择。因为我今天只熟悉了哈希表的内部工作原理,所以继续我对它的理解,所以如果我错了请纠正我:

哈希表的常量大小等于其哈希函数输出大小中可存储的最大值*键值对大小*桶大小+溢出桶大小。因此,例如,如果散列函数产生16位哈希值且桶大小为4且值为32位则则为2 ^ 16 * 4 * 6 = 1572864或1.5MB加上溢出。

这实质上会使哈希表成为一种压缩查找表。如果散列函数发生更改,则必须重新评估整个表。否则它只是添加东西到空槽。此外,哈希表可以包含其散列大小可以解决的最大单位(因此对于16位散列而言,其为65536)但是如果没有很多冲突就能很好地执行,那么它必须要少得多。

好的,这是我试图索引的东西:(最多)1亿对,64位整数键和96位值。键是对象ID(主要是短序列,但可以遍布整个地方),值是对象位置+长度。读/写同样重要且非常频繁。

我调查的其他选项是各种树,但我不喜欢它们的原因是因为在我看来我必须做很多稀疏的读/写来查找数据或重构树我每次进去都会。

所以这是我的问题:

  1. 在我看来,我需要一个带有奇怪位数的哈希值,我想到的是〜38,因为它只是我可以存储在单个磁盘上的最大值,并且应该足够舒适1亿。这个奇怪的位数是闻所未闻的吗?我想我在CPU之前可能会遇到磁盘活动方式的瓶颈。
  2. 那里有关于如何为我的特定案例设计好哈希函数的文章吗?谷歌搜索给了我一个常见方法的概述,但我正在寻找它们背后的解释。
  3. 我应该知道的任何其他一般提示/陷阱?

1 个答案:

答案 0 :(得分:0)

  

哈希表具有常量

...不一定 - 哈希表可以支持调整大小,但这往往是在相当戏剧性和侵入性的块中完成的,你可以在其中推理哈希表,就像它在前后都是恒定大小一样。

  

...相当于其散列函数输出大小可存储的最大值*键值对大小*桶大小+溢出桶大小。因此,例如,如果散列函数产生16位哈希值且桶大小为4且值为32位则则为2 ^ 16 * 4 * 6 = 1572864或1.5MB加上溢出。

完全没有。计算大小的更好方法是说有一定大小的N值,并且你想要保持容量:大小比例介于3:1和5:4之间:表内存使用率为:N * sizeof(Value )*比率。

散列值中的位数仅与之相关,因为它表示您可以散列到的不同存储桶的最大数量:如果您尝试使用更大的表,那么您将获得比您更多的冲突哈希函数生成更宽位的哈希值。如果您的哈希函数中的位数多于您需要的,这不是问题,例如,将模数与当前表格大小一起找到您的存储桶:hashed_to_bucket = hash_value % num_buckets

  

这实质上会使哈希表成为一种压缩查找表。

这是查看哈希表的好方法。

  

如果哈希函数发生变化,则必须重新评估整个表。否则它只会向空插槽添加内容。

绝对重新评估/重新生成。否则,添加到空槽只是不良后果之一。

  

此外,哈希表可以包含其散列大小可以解决的最大单位(因此对于16位散列而言,其为65536)但是如果没有多次冲突就能很好地执行,那么它就必须少得多。

如上所述,那个(例如65536)不是 hard 最大值,但是"在没有碰撞的情况下表现良好"应该避免过去。为了表现良好, 必须要少得多:如果它是一个高质量的16位散列函数,那么任何高达65536的东西都是完美的。

  

好的,这是我试图索引的东西:(最多)1亿对64位整数键和96位值。键是对象ID(主要是短序列,但可以遍布整个地方),值是对象位置+长度。读/写同样重要且非常频繁。

     

我调查的其他选项是各种树,但我不喜欢它们的原因是因为在我看来我必须做很多稀疏的读/写来查找数据或重组我每次进去都会树。

可能......很大程度上取决于您的访问模式。例如,如果您碰巧尝试按照"短序列"来访问密钥。那么一个数据组织模型往往会把它们放在内存/磁盘附近。某些类型的树结构做得很好,你有时也可以破解你的哈希函数来执行它(但需要平衡它以防止碰撞)。

  

在我看来,我需要一个带有奇怪位数的哈希,我想到了〜38,因为它只是我可以存储在单个磁盘上的最大值,应该是舒适的足够1亿。这个奇怪的位数是闻所未闻的吗?我认为在CPU之前我可能会遇到磁盘活动方式的瓶颈。

不是这样......你有64位整数键 - 需要64位或更大的哈希值。也就是说,32位散列也可能很好 - 产生40亿个不同的值,大于你的1亿个键。

  

关于如何为我的特定情况设计好的哈希函数,是否有任何文章?谷歌搜索给了我一个常见方法的概述,但我正在寻找它们背后的解释。

不是我意识到的。

  

我应该知道的任何其他一般提示/陷阱?

对于提示......我说开始简单(例如,使用哈希函数返回密钥不变并使用具有哈希表容量的模数'素数,或者使用任何公共哈希,如果你&# 39;重新获取一个哈希表实现,使用例如2个幂的数据桶)并测量你的碰撞率:它告诉你它在改善你的哈希方面需要付出多少努力。

一种非常简单的方法来理解,随机化"在你的情况下哈希是有8个256个32位整数的表 - 用硬编码的随机数初始化(你可以谷歌随机数下载网站)。给定任何64位密钥,只需将其切成8个字节,然后将每个字节用作连续表中的密钥,对您查找的32位值进行异或。 64位输入位中的任何一位的单位差异将以相等的概率影响散列值中的所有32位。

uint32_t table[8][256] = { ...add some random numbers... };

uint32_t h(uint64_t n)
{
    uint32_t result = 0;
    unsigned char* p = (unsigned char*)&n;

    for (int i = 0; i < 8; ++i)
        result ^= table[i][*p++];

    return result;
}