设计rehash函数...如何避免相同的哈希?

时间:2014-03-17 21:28:55

标签: c++ hashtable

所以我正在开发一个使用哈希表的程序。这是怎么回事:

1)将文本文件读入“符号”对象的向量(包含名称和数字) 2)散列Symbol对象的名称。 3)将此对象插入哈希表。

到目前为止,我一直将唯一生成的哈希键存储为整数数组。然后我循环遍历数组,看看是否有重复。

如果有,我知道发生了碰撞。这个方法已被证明是成功的,但是现在我必须编写一个rehash()函数,这样我就可以获得一个不会导致冲突的新密钥。但我无法弄清楚如何做到这一点。

我已经包含了我的循环,我检查了键的数组和我当前的散列函数以及输出。任何关于去哪里的建议都会非常感激。

for (int i=0; i < TABLE_SIZE; i++)
        {
              if (i != j)
              {
                if (array[i] == array[j])
                {
                    cout << endl;
                      cout << "Collision occurred at" << array[i] << endl;
                    cout << "Now rehashing..." << endl;
                    // REHASH FUNCTION SHOULD GO HERE --> rec.key = rehash(data);
                    cout << "The new key is: " << rec.key << endl;
                    break;
                }
              }
        }

        dataTable.insert(rec); //inserts the record object into the HashTable.

现有哈希函数:

int hasher (string data)
  // POST: the index of entry is returned
  {       int sum = 0;
          for (int k = 0; k < data.length(); k++)
              sum = sum + int(data[k]);
          return  sum % TABLE_SIZE;
  }

输出我正在:

计数       2       此Symbol对象的关键是:7

  num
  2
  The key for this Symbol object is: 0


  Collision occurred at0
  Now rehashing...
  The new key is: 1
  myFloat
  4
  The key for this Symbol object is: 18

  myDouble
  5
  The key for this Symbol object is: 14

  name
  6
  The key for this Symbol object is: 18


  Collision occurred at18
  Now rehashing...
  The new key is: 1
  address
  6
  The key for this Symbol object is: 7


  Collision occurred at7
  Now rehashing...
  The new key is: 1
  salary
  5
  The key for this Symbol object is: 1

  gpa
  4
  The key for this Symbol object is: 18


  Collision occurred at18
  Now rehashing...
  The new key is: 1
  gdp
  5
  The key for this Symbol object is: 0


  Collision occurred at0
  Now rehashing...
  The new key is: 1
  pi
  5
  The key for this Symbol object is: 7


  Collision occurred at7
  Now rehashing...
  The new key is: 1
  city
  6
  The key for this Symbol object is: 0


  Collision occurred at0
  Now rehashing...
  The new key is: 1
  state
  6
  The key for this Symbol object is: 20

  county
  6
  The key for this Symbol object is: 2

  ch
  0
  The key for this Symbol object is: 14


  Collision occurred at14
  Now rehashing...
  The new key is: 1
  ch2
  0
  The key for this Symbol object is: 1


  Collision occurred at1
  Now rehashing...
  The new key is: 1
  ID
  1
  The key for this Symbol object is: 15

  studentID
  1
  The key for this Symbol object is: 13

  max
  3
  The key for this Symbol object is: 11

  max2
  3
  The key for this Symbol object is: 19

  greeting
  6
  The key for this Symbol object is: 13


  Collision occurred at13
  Now rehashing...
  The new key is: 1
  debt
  5

如您所见,当发生碰撞时,它会成功检测到它。现在我只需要一种方法来再次重新使用密钥,以便将来不会发生...因为现在重新散列也是一种冲突。

5 个答案:

答案 0 :(得分:2)

解决哈希冲突的简单方法是separate chaining。基本上,您的数据结构有一堆链表,因此当发生冲突时,您只需将结果附加到此处发生冲突的其他值。还有其他方法具有更高效的插入/查找时间,但显然,它们需要更多的努力来实现。

答案 1 :(得分:1)

说你的数组是:

#define N ...
struct element table[N];

然后你可以定义两个哈希函数(独立!),让我们说int h(data); int g(data); 0 <= h < N1 <= g < N。确保g返回的值相对于N的素数。然后,要插入新元素,请执行以下操作:

int i = h(data);
if(table[i] is free)
     /* Go ahead! */
else {
     /* Was occupied, try alternatives */
     int j = g(data);
     for(k = i + j; k != i; k = (k + j) % N)
         if(table[k] is free) {
             /* Found a free space, go ahead */
             break;
         }
     if(k == i) {
         /* Table is full */
     }
}

搜索类似。

g的值始终相对优先N的最简单方法是采用1.稍微更难确保N为素数,{{1} }总是小于g

使用N会使数据聚集起来,从而减慢搜索速度;不同的g == 1值可以避免这种情况。

答案 2 :(得分:1)

  

所以我正在研究一个使用哈希表的程序。这是怎么回事:

     

1)将文本文件读入&#34;符号&#34;的矢量。对象(包含名称和数字)2)散列符号对象的名称。 3)将此对象插入哈希表。

     

到目前为止,我一直将唯一生成的哈希键存储为整数数组。然后我循环遍历数组,看看是否有重复。

     

如果有,我知道那里发生了碰撞。这种方法已被证明是成功的,但是现在我必须写一个rehash()函数,这样我就可以获得一个不会导致碰撞的新密钥。

我不确定您是否正在尝试实施NIST呼叫double-hashing2-choice hashingcuckoo hash的内容。我认为你在谈论双重哈希。

如果你知道所有可能的输入,你可以选择两个具有你正在寻找的属性的哈希。当然,如果您知道每个可能的输入,您可以选择一个没有任何冲突的哈希。

如果您不关心性能,可以选择两种不同的加密安全散列函数,例如SHA3和Skein。但是,我非常怀疑你是否想要这样。当博客讨论the meet-in-the-middle vulnerability in a widely-used hash function时,我认为最简单的解决方案是使用一种功能 - 例如加密安全散列函数 - 设计为抗冲突。我所知道的最简单的哈希函数,我认为适合该法案TEA,比它要替换的函数慢十倍。请注意:TEA对其他任何东西都不安全,因为它对于哈希映射来说是一个糟糕的选择,所以很难找到任何用途。

你可以选择两个狂野的different hash functions并希望最好。

SipHash,由密码学家设计,即使它不具有加密安全性,也具有抗冲突性。但是,not everyone's happy with its performance(部分&#34;动态语言的哈希函数&#34;)。另外,我对SipHash竞赛中的任何类似分析并不熟悉,所以虽然我很乐意将你指向SipHash,但我还是不习惯推荐第二个哈希。

我更愿意使用不同的方法来处理碰撞。你想做的事情并没有错,但是它已经走了很多路,很难找到好的建议。

所以,我的推荐(按优先顺序)是:

  • 使用std::unordered_map。不幸的是,当您可以替换std::unordered_map使用的哈希函数时,这很痛苦。
  • 使用单个哈希函数,并使用单独的链接处理冲突(使用std::vector(对于CPU缓存很好,添加/删除元素会更麻烦)或std::list用于列表)。
  • 使用单个哈希函数,并处理与linear probing的冲突,至少这将与您的CPU缓存配合良好。
  • 如果所有其他方法都失败了,请使用双哈希,选择一个您知道易受第一个哈希冲突的快速哈希算法,并选择SipHash来处理冲突。如果碰撞很少,你就会有一个快速的哈希映射,如果有很多冲突,依靠SipHash来处理它们是合理的。如果您对碰撞感到偏执,则应假设每个插入或检索都会调用两个散列函数。您必须决定是否可以接受。
  

此方法已被证明是成功的,但是现在我必须编写一个rehash()函数,这样我就可以得到一个不会导致碰撞的新密钥。但我无法弄清楚如何做到这一点。

     

我已经包含了我的循环,我检查了键数组和我当前的散列函数以及输出。任何关于去哪里的建议都会非常感激。

简而言之:

  • 插入时,检查插槽是否为空:
    • 如果插槽为空,请将项目放在那里(完成后)
    • 如果插槽中已有内容,请调用原始密钥上的rehash()方法 ,如果rehash()建议的插槽为空,则放置该项那里;否则,请致电rehash(rehash(key)),然后继续这样做,直到找到一个空插槽(至少,这是我如何在双重散列上取消NIST页面)
  • 检索时,您必须先找到一个空槽,然后才能说出某个项目不在地图中;例如,如果hash(key)返回一个空插槽,则表示您已完成;如果hash(hey)返回包含您要查找的项目的广告位,则表示您已完成;它hash(key)会返回包含不同商品的广告位,然后您必须致电rehash(key)rehash(rehash(key))等,直到找到您正在寻找的内容或找到空位< / LI>
  • 从哈希地图中删除项目时,您可能想要使用墓碑说'#34;我删除了这里的内容,但您需要继续调用rehash()来查看如果您要查找的元素在地图中<#34;
  • 在增长哈希时,您基本上创建一个更大的容器,并一次插入一个元素

如果这看起来令人生畏,请重新考虑std::unordered_map是否适合该法案。


我现在可以推荐除SipHash之外的抗冲突哈希(或者,事实上,several)。

答案 3 :(得分:0)

rehash可能很简单,比如在表大小中添加一个相对素数的数字到索引(开放寻址)。将密钥添加到哈希表时,可能需要多次重新查找哈希表中的可用索引。

答案 4 :(得分:0)

当你看到实现一个没有冲突的哈希函数时,你需要多年的研究。总而言之,你正试图实现完美的哈希。

您实施的内容称为链接解决冲突。 这个过程的缺点是每当发生碰撞时,搜索采用O(n)的最坏情况时间,其中n是子链中元素的数量

选择哈希函数

以下是选择has函数的方法

1)分工方法
2)乘法方法
3)通过开放式寻址解决冲突 4)探索策略
5)普遍哈希。 (碰撞概率无限低的情况)
6)完美的哈希。 (只有在某些情况下才有可能)。

请参阅第7讲和第8讲 http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/video-lectures/
为了更深入地了解上述方法,如果您有兴趣编写自己的通用哈希函数,我认为您从讲座中获得的知识将是一个很好的起点。

一切顺利