理解Blizzard Hash

转自:http://blog.csdn.net/leeeryan/article/details/5978402?reload


1.一个Hash算法首先要有一个Hash表:

[cpp]  view plain copy
  1. //////////////////////////////////////////////////////////////////////////    
  2. // 哈希索引表定义    
  3. typedef struct  _HASHTABLE  
  4. {    
  5.     long nHashA;    
  6.     long nHashB;    
  7.     bool bExists;    
  8. }HASHTABLE, *PHASHTABLE ;    
  9. m_tablelength = nTableLength;    
  10.     //初始化hash表  
  11.     m_HashIndexTable = new HASHTABLE[nTableLength];    
  12.     for ( int i = 0; i < nTableLength; i++ )    
  13.     {    
  14.         m_HashIndexTable[i].nHashA = -1;    
  15.         m_HashIndexTable[i].nHashB = -1;    
  16.         m_HashIndexTable[i].bExists = false;    
  17.     }          

2.还要有一个压缩算法,把字符串压缩成32位无符号整数:

[cpp]  view plain copy
  1. unsigned long StringHash::HashString(const string& lpszString, unsigned long dwHashType)    
  2. {     
  3.     unsigned char *key = (unsigned char *)(const_cast<char*>(lpszString.c_str()));    
  4.     unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;    
  5.     int ch;    
  6.     while(*key != 0)    
  7.     {     
  8.         ch = toupper(*key++);    
  9.         seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);    
  10.         seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;     
  11.     }    
  12.     return seed1;     
  13. }   
  3.在上面那个压缩算法中有一个cryptTable,我们要先把它处理一下:
[cpp]  view plain copy
  1. void StringHash::InitCryptTable()    
  2. {     
  3.     unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;    
  4.     for( index1 = 0; index1 < 0x100; index1++ )    
  5.     {     
  6.         for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )    
  7.         {     
  8.             unsigned long temp1, temp2;    
  9.             seed = (seed * 125 + 3) % 0x2AAAAB;    
  10.             temp1 = (seed & 0xFFFF) << 0x10;    
  11.             seed = (seed * 125 + 3) % 0x2AAAAB;    
  12.             temp2 = (seed & 0xFFFF);    
  13.             cryptTable[index2] = ( temp1 | temp2 );     
  14.         }     
  15.     }     
  16. }    

4.现在就可以Hash一个字符串了,我们调用了三次压缩算法,获取了三个hash值,第一个hash值用来计算其在hash表中的索引,还有两个用来处理碰撞情况(两个不同的字符串三次hash的结果是一样的概率基本为0)

[cpp]  view plain copy
  1. bool StringHash::Hash(  
  2.                                         string lpszString, //url  
  3.                                         const PLinkNode node     //url 对应节点  
  4.                                         )  
  5. {    
  6.     const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;    
  7.     unsigned long nHash = HashString(lpszString, HASH_OFFSET);    
  8.     unsigned long nHashA = HashString(lpszString, HASH_A);    
  9.     unsigned long nHashB = HashString(lpszString, HASH_B);    
  10.     unsigned long nHashStart = nHash % m_tablelength,   
  11.         nHashPos = nHashStart;    
  12.     while ( m_HashIndexTable[nHashPos].bExists)    
  13.     {     
  14.         nHashPos = (nHashPos + 1) % m_tablelength;    
  15.         if (nHashPos == nHashStart) //一个轮回    
  16.         {    
  17.             //hash表中没有空余的位置了,无法完成hash  
  18.             return false;     
  19.         }    
  20.     }    
  21.     m_HashIndexTable[nHashPos].bExists = true;    
  22.     m_HashIndexTable[nHashPos].nHashA = nHashA;    
  23.     m_HashIndexTable[nHashPos].nHashB = nHashB;    
  24.     return true;    
  25. }    
5.我们还需要一个接口,用来判断一个字符串是否被hash过
[cpp]  view plain copy
  1. unsigned long StringHash::Hashed(const string& url)    
  2. {     
  3.     const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;    
  4.     //不同的字符串三次hash还会碰撞的几率无限接近于不可能  
  5.     unsigned long nHash = HashString(url, HASH_OFFSET);    
  6.     unsigned long nHashA = HashString(url, HASH_A);    
  7.     unsigned long nHashB = HashString(url, HASH_B);    
  8.     unsigned long nHashStart = nHash % m_tablelength,    
  9.     nHashPos = nHashStart;    
  10.     while ( m_HashIndexTable[nHashPos].bExists)    
  11.     {     
  12.         if (m_HashIndexTable[nHashPos].nHashA == nHashA && m_HashIndexTable[nHashPos].nHashB == nHashB)     
  13.             return nHashPos;     
  14.         else     
  15.             nHashPos = (nHashPos + 1) % m_tablelength;    
  16.         if (nHashPos == nHashStart)     
  17.             break;     
  18.     }    
  19.     return -1; //没有找到    
  20. }    
  至此,一个简单的字符串hash算法就完成了。

 ---------------------------------------------------------------------------------------------------------------------------------------------------

总结:

 一开始对下面的代码不是很理解

[cpp]  view plain copy
  1. while ( m_HashIndexTable[nHashPos].bExists)    
  2.     {     
  3.         nHashPos = (nHashPos + 1) % m_tablelength;    
  4.         if (nHashPos == nHashStart) //一个轮回    
  5.         {    
  6.             //hash表中没有空余的位置了,无法完成hash  
  7.             return false;     
  8.         }    
  9.     }  
  其实,就是一个hash值与其他的碰撞了,那就先来后到,挑下面相邻的地方;这并不会影响Hashed()的匹配,匹配时也是按照这样的方法,并且还要匹配那两个hash值,有点像从一个房间开始,挨个敲门,看里面是不是要找的人!

所以说这是一个空间换时间的算法,hash表越大,发生碰撞的可能性就越小,算法复杂度就越接近于O(1);


【yasi】更多信息见

http://www.oschina.net/code/snippet_99767_1217

http://blog.csdn.net/eaglewood2005/article/details/4394583

你可能感兴趣的:(理解Blizzard Hash)