FNV哈希

来源:互联网 发布:windows repair 编辑:程序博客网 时间:2024/05/28 09:33

转自: http://www.cnblogs.com/napoleon_liu/archive/2010/12/26/1917396.html

 

我这个人习惯现学现卖,上一篇AC自动机就是,刚才看看浏览次数七十多了(虽然写的不详细),不禁惭愧,希望对其他人能有帮助(哪怕是微乎其微的)。

最近两天在学习哈希算法,从一个英文网站上(只要不嫌麻烦的一句一句翻译英文,读起来还是很爽的)。

市面上的哈希算法应该有很多种。FNV是第一种我真正接触哈希算法,算法简单。

简单介绍一下(其实就是翻译一下,汗!):

  FNV哈希函数,有三种FNV-0(已废弃),FNV-1, FNV-1a。

  FNV-1和FNV-1a算法对于最终生成的哈希值(hash)有一定限制

  1,hash是无符号整型

  2,hash的位数(bits),应该是2的n次方(32,64,128,256,512,1024),一般32位的就够用了。

  FNV-1形式:

[java] view plaincopyprint?
  1. hash = offset_basis   
  2. for each octet_of_data to be hashed   
  3. hash = hash * FNV_prime   
  4. hash = hash xor octet_of_data   


 

   FNV-1a形式:

[java] view plaincopyprint?
  1. hash = offset_basis   
  2. for each octet_of_data to be hashed   
  3. hash = hash xor octet_of_data   
  4. hash = hash * FNV_prime   
  5. return hash  



区别是有两句操作顺序调换,产生FNV-1a的原因是,有些人使用FNV-1a代替FNV-1发现算法离散性或CPU利用效率更好(我感觉应该没什么太大差距,只是微小的)。

for each octet_of_data to be hashed 意思是对于你要算哈希值的数,它的每一个字节。

hash = hash * FNV_prime,是包含取模运算的,具体看你采用多少位的哈希函数。例如,你用32为哈希,hash = hash * FNV_prime % (2的32次方);

hash = hash xor octet_of_data,意思是把当前取来的字节和当前的hash值的第八位做抑或运算。

32 bit FNV_prime = 224 + 28 + 0x93 =16777619

64 bit FNV_prime = 240 + 28 + 0xb3 =1099511628211

128 bit FNV_prime = 288 + 28 + 0x3b =309485009821345068724781371

256 bit FNV_prime = 2168 + 28 + 0x63 =374144419156711147060143317175368453031918731002211

512 bit FNV_prime = 2344 + 28 + 0x57 =
35835915874844867368919076489095108449946327955754392558399825615420669938882575
126094039892345713852759


1024 bit FNV_prime = 2680 + 28 + 0x8d =
50164565101131186554345988110352789550307653454047907443030175238311120551081474
51509157692220295382716162651878526895249385292291816524375083746691371804094271
873160484737966720260389217684476157468082573

  以上这几个数都是质数(哈希的理论基石,质数分辨定理,我理解也不深),不用管为什么,用的时候照搬就是了。

  

 如果我想得到的哈希位数不是上面几种呢?

  比如我想得到24位的哈希值,方法:取上面比24大的最小的位数,当然是32了,先算对应32位哈希值,再转换成24位的。

  转换方法:32 - 24 = 8, 好了把得到的32砍成两段,高8位最和低24位。第8位与低24位中的低8位做抑或,得到的24位值是最终结果。(hash>>24) ^ (hash & 0xFFFFFF);
 如果我想得到的哈希值不能用位数来表示呢?

  比如想得到范围在0~9999的哈希值,方法:取上面比9999大的最小的位数,当然是32,先算对应32位哈希值,再mod(9999 +1)。简单吧!!

  其实还有一种方法,可以避免上面方法出现的某些问题(映射分布有点儿不均匀,这个问题在一般情况下不用考虑,所以方法也不介绍了,有兴趣可以去网站上看看)。

【英文参考】

http://www.isthe.com/chongo/tech/comp/fnv/index.html 

http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash


 

FNV是 Glenn Fowler, Landon Curt Noll, and Phong Vo 三人的缩写。

FNV-1 哈希算法的核心思想如下:

 hash = offset_basis
 for each octet_of_data to be hashed    hash = hash * FNV_prime    hash = hash xor octet_of_data   return hash

实现源码


    uint32_t fnv_hash(char const *str, int len)
    {
        unsigned long hash = 2166136261; //offset_basis
//FNV prime for 32 bit is 16777619
//#define FNV_OP()  hash = (hash*16777619)^*str++
#define FNV_OP()  hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);\
          hash ^=*str++;
        for (; len >= 8; len -= 8) {
            FNV_OP(); //1
            FNV_OP(); //2
            FNV_OP(); //3
            FNV_OP(); //4
            FNV_OP(); //5
            FNV_OP(); //6
            FNV_OP(); //7
            FNV_OP(); //8
       }
       switch (len) {
           case 7: FNV_OP(); /* fallthrough... */
           case 6: FNV_OP(); /* fallthrough... */
           case 5: FNV_OP(); /* fallthrough... */
           case 4: FNV_OP(); /* fallthrough... */
           case 3: FNV_OP(); /* fallthrough... */
           case 2: FNV_OP(); /* fallthrough... */
           case 1: FNV_OP(); break;
           case 0: break;
       }
       return hash;
   }

 c++ 0x 标准 tr1中的实现 (gcc代码)

  

  // Dummy generic implementation (for sizeof(size_t) != 4, 8).
  template<std::size_t = sizeof(std::size_t)>
    struct Fnv_hash
    {
      static std::size_t
      hash(const char* first, std::size_t length)
      {
        std::size_t result = 0;
        for (; length > 0; --length)
          result = (result * 131) + *first++;
        return result;
      }
    };

  template<>
    struct Fnv_hash<4>
    {
      static std::size_t
      hash(const char* first, std::size_t length)
      {
        std::size_t result = static_cast<std::size_t>(2166136261UL);
        for (; length > 0; --length)
          {
            result ^= (std::size_t)*first++;
            result *= 16777619UL;
          }
        return result;
      }
    };

  template<>
    struct Fnv_hash<8>
    {
      static std::size_t
      hash(const char* first, std::size_t length)
      {
        std::size_t result = static_cast<std::size_t>(14695981039346656037ULL);
        for (; length > 0; --length)
          {
            result ^= (std::size_t)*first++;
            result *= 1099511628211ULL;
          }
        return result;
      }
    };

FNV哈希 通用性很好,time33只针对英文文本比较合适。

 

FNV哈希应用于很多地方:

calc
Domain Name Servers
mdbm key/value data lookup functions
Database indexing hashes
major web search / indexing engines
high performance EMail servers
Netnews history file Message-ID lookup functions
Anti-spam filters
NFS implementations (e.g., FreeBSD 4.3, IRIX, Linux (NFS v4))
Cohesia MASS project server collision avoidance
spellchecker programmed in Ada 95
flatassembler's open source x86 assembler - user-defined symbol hashtree
PowerBASIC inline assembly routine
text based referenced resources for video games on the PS2, Gamecube and XBOX
non-cryptographic file fingerprints
FRET - a tool to identify file data structures / helps to understand file formats
used to in the process of computing Unique IDs in DASM (DTN Applications for Symbian Mobile-phones)
Used by Microsoft in their hash_map implementation for VC++ 2005
Used in an implementation of libketama for use in items such as memcache.
Used in the realpath cache in PHP 5.x (php-5.2.3/TSRM/tsrm_virtual_cwd.c).
Used to improve the fragment cache at twitter (see slide 31).

 

原创粉丝点击