【问题标题】:Writing a robust integer hash function编写一个健壮的整数散列函数
【发布时间】:2013-06-21 16:04:33
【问题描述】:

我想为哈希表编写一个好的整数哈希函数。即使我怀疑我的哈希表不会太大(比如大小为 36 个元素),生成哈希值的“键”可能会在 0,20,31,.... 11456,13444 等范围内的值之间发生巨大变化. 之前已经在这里发布过类似的问题,我的哈希函数的灵感来自here提供的建议答案。

以下是我的表的结构:

  typedef struct _list_t_ {
  int key;
  int value;
  struct _list_t_ *next;
  } list_t;

  typedef struct _hash_table_t_ {
  int size;       /* the size of the table */
  list_t **table; /* the table elements */
  } hash_table_t;

以下是我目前的哈希函数:

  unsigned int hash(hash_table_t *hashtable, int key)
  {
   unsigned int hashval;

   hashval = 0;
   hashval = key;
   hashval = ((hashval >> 16) ^ hashval) * 0x45d9f3b;
   hashval = ((hashval >> 16) ^ hashval) * 0x45d9f3b;
   hashval = ((hashval >> 16) ^ hashval);
   return hashval % hashtable->size; // MOD done to keep within the range of the table size
   }

如上所述,生成哈希值的“键”变化很大(值范围从 0、20、31、...11456、13444 等)。问题是我注意到这个散列函数非常频繁地生成相同的散列值。有没有办法可以调整它,以便以新的哈希值结尾的机会更多。

【问题讨论】:

  • 很难写出好的散列函数。使用已经过良好测试的现有的。
  • 有几个通用哈希函数及其实现,here
  • 嗯,它可能很糟糕,但你客观地测试过吗?如果您盯着仅包含 36 个唯一符号的强随机输出,您一定会在其中看到重复的模式。这就是人类大脑的工作方式。这并不意味着散列被破坏了;它只是受到输出范围的过度限制。当然,如果输入不是唯一的,那么输出不能是唯一的。

标签: c hash hashtable


【解决方案1】:
 unsigned int hash(hash_table_t *hashtable, int key)

这是创建完美散列函数的相当难得的机会。为每个不同的输入值生成唯一值的函数。你不可能做得更好。在这种情况下是可能的,因为输入位数等于输出位数。典型的散列函数需要处理更多的输入位和有限数量的输出位。这造成了不可避免的哈希冲突问题。完美的哈希没有这样的问题。

在这种情况下,完美散列函数一如既往地是微不足道的:

 unsigned int getslot(hash_table_t *hashtable, int key)
 {
   return (unsigned)key % hashtable->size;
 }

请注意,您需要区分散列函数和将散列映射到槽或桶的代码。我将它们组合在一个函数中,因为它们是如此微不足道并给了它一个正确的名称。另请注意,像您所做的那样添加任何熵都是没有意义的,结果不能比原来的分布更好。只有当您有更多的输入值并且它们可以相关时才有意义。

【讨论】:

  • 如果许多键是哈希表大小的倍数(例如,大小为 128 的哈希表中 8 的倍数)怎么办?使用这种方法,您最终会遇到许多冲突,因此对输入位进行加扰仍然是必不可少的。
  • 任何哈希表实现的一个基本规则是槽数是质数。没有关于散列的文献没有指出这一点。
  • 实践中的许多实现使用两个或其他组合的幂来简化调整大小,例如标准 Java 库中的 HashMap。一个体面的哈希码实现使这不是问题。
  • this implementation 中 GetPrime() 的使用就是一个很好的例子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-28
  • 2010-11-30
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
相关资源
最近更新 更多