经常使用的hash函数

简介

哈稀函数按照定义能够实现一个伪随机数生成器(PRNG),从这个角度能够获得一个公认的结论:哈希函数之间性能的比较能够经过比较其在伪随机生成方面的比较来衡量。
一些经常使用的分析技术,例如 泊松 分布可 用于分析不一样的哈希函数对不一样的数据的碰撞率(collision rate) 。通常来讲,对任意一类的数据存在一个理论上完美的哈希函数 这个 完美的 哈希函数 定义是 没有 发生任何 碰撞,这 意味着没有 出现 重复 的散列 在现实中 很难 找到一个完美的 哈希 散列 函数,并且这种完美函数的趋近变种在 实际应用中的做用 是至关 有限的。 在实践中 人们广泛 认识到, 一个完美 哈希函数 的哈希 函数 ,就是在 一个 特定的数据 集上产生的 的碰撞 最少哈希的函数。
如今的问题是有各类类型的数据,有一些是高度随机的,有一些有包含高纬度的图形结构,这些都使得找到一个通用的哈希函数变得十分困难,即便是某一特定类型的数据,找到一个比较好的哈希函数也不是意见容易的事。咱们所能作的就是经过试错方法来找到知足咱们要求的哈希函数。能够从下面两个角度来选择哈希函数:
1.数据分布
 一个衡量的措施是考虑一个哈希函数是否能将一组数据的哈希值进行很好的分布。要进行这种分析,须要知道碰撞的哈希值的个数,若是用链表来处理碰撞,则能够分析链表的平均长度,也能够分析散列值的分组数目。
2.哈希函数的效率
另个一个衡量的标准是哈希函数获得哈希值的效率。一般,包含哈希函数的算法的算法复杂度都假设为O(1),这就是为何在哈希表中搜索数据的时间复杂度会被认为是"平均为O(1)的复杂度",而在另一些经常使用的数据结构,好比图(一般被实现为红黑树),则被认为是O(logn)的复杂度。
一个好的哈希函数必修在理论上很是的快、稳定而且是可肯定的。一般哈希函数不可能达到O(1)的复杂度,可是哈希函数在字符串哈希的线性的搜索中确实是很是快的,而且一般哈希函数的对象是较小的主键标识符,这样整个过程应该是很是快的,而且在某种程度上是稳定的。
在这篇文章中介绍 的哈希 函数 被称为 简单的哈希 函数。 它们一般 用于 散列( 哈希 字符串 )数据 它们被用来 产生一种 在诸如 哈希 表的 关联容器 使用的key 这些 哈希 函数不是 密码安全的 很容易经过 颠倒 和组合不一样数据的 方式 产生 彻底相同的哈希 值。

哈希方法学

哈希函数一般是由他们产生哈希值的方法来定义的,有两种主要的方法:
1.基于加法和乘法的散列
这种方式是经过遍历数据中的元素而后每次对某个初始值进行加操做,其中加的值和这个数据的一个元素相关。一般这对某个元素值的计算要乘以一个素数。

基于加法和乘法的散列

 

2.基于移位的散列 html

和加法散列相似,基于移位的散列也要利用字符串数据中的每一个元素,可是和加法不一样的是,后者更多的而是进行位的移位操做。一般是结合了左移和右移,移的位数的也是一个素数。每一个移位过程的结果只是增长了一些积累计算,最后移位的结果做为最终结果。 java


基于移位的散列

 

 

哈希函数和素数 算法

没有人能够证实素数和伪随机数生成器之间的关系,可是目前来讲最好的结果使用了素数。伪随机数生成器如今是一个统计学上的东西,不是一个肯定的实体,因此对其的分析只能对整个的结果有一些认识,而不能知道这些结果是怎么产生的。若是能进行更具体的研究,也许咱们能更好的理解哪些数值比较有效,为何素数比其余数更有效,为何有些素数就不行,若是能用可再现的证实来回答这些问题,那么咱们就能设计出更好的伪随机数生成器,也可能获得更好的哈希函数。 编程

围绕着哈希函数中的素数的使用的基本的概念是,利用一个素质来改变处理的哈希函数的状态值,而不是使用其余类型的数。处理这个词的意思就是对哈希值进行一些简单的操做,好比乘法和加法。这样获得的一个新的哈希值必定要在统计学上具备更高的熵,也就是说不能有为偏向。简单的说,当你用一个素数去乘一堆随机数的时候,获得的数在bit这个层次上是1的几率应该接近0.5。没有具体的证实这种不便向的现象只出如今使用素数的状况下,这看上去只是一个自我宣称的直觉上的理论,并被一些业内人士所遵循。 缓存

决定什么是正确的,甚至更好的方法和散列素数的使用最好的组合仍然是一个颇有黑色艺术没有单一的方法能够宣称本身是最终的通用散列函数。最好的所能作的就是经过试错演进和得到适当的散列算法,以知足其须要的统计分析方法。 安全


位偏向 服务器

位序列发生器是纯粹随机的或者说在某种程度上肯定性的,能够按照必定的几率产生某种状态或相反状态的比特,这个几率就是位偏向。在纯粹随机的状况下,产生高位或者低位的位偏向应该是50%。 网络

而后在伪随机产生器中,算法将决定在产生器在最小输出模块的位偏向。 数据结构


位偏向

 

假设一个PRNG的产生8做为其输出块。出于某种缘由,MSB始终是设置为高,MSB的位偏向将是100%的几率置高这一结论是,即便有256个PRNG的产生可能的值,小于128将永远不会产生。简单起见,假设其余位正在生成纯粹是随机的,那么平等的机会,128和255之间的任何值将产生,可是在同一时间,有0%的机会,一个小于128的值会产生 函数

全部PRNGs,不管是杂凑函数,密码,msequences或其余任何产生比特流的产生器都会有这样一个位偏向大多数PRNGs他们将尝试收敛位偏向到一个肯定值流密码就是一个例子,而其余产生器在不肯定的位偏向下效果更好。

混合位序列加扰是一种产生在一个共同的平等流位偏向的方法。虽然咱们必须要当心,以确保他们不会混合至发散偏向。密码学中的一个混合使用的形式被称为雪崩,这就是一个位块使用另外一个块来替换或置换混合在一块儿,而另外一块产生与其余快混合的输出。

正以下图中显示的,雪崩过程始于一个或多个二进制数据块。对数据中的某些位操做一般是一些输入敏感位减小位逻辑)生产第i数据。而后重复这个过程是第i数据,以生成一个i+1层数据,是当前层的位数小于或等于的位数

这一反复的过程致使一个依靠以前数据全部位的位应该指出的是,下图是一个单纯归纳,雪崩过程不必定这一进程的惟一形式。

雪崩过程

 

 

 

各类形式的哈希

 

哈希是一个在现实世界中将数据映射到一个标识符的工具,下面是哈希函数的一些经常使用领域:

1.字符串哈希

在数据存储领域,主要是数据的索引和对容器的结构化支持,好比哈希表。

2.加密哈希

用于数据/用户核查和验证。一个强大的加密哈希函数很难从结果再获得原始数据加密哈希函数用于哈希用户的密码,用来代替密码自己存在某个服务器撒很难过加密哈希函数也被视为不可逆的压缩功能,可以表明一个信号标识大量数据,能够很是有用的判断当前的数据是否已经被篡改(好比MD5),也能够做为一个数据标志使用,以证实经过其余手段加密文件的真实性

3.几何哈希

这个哈希表用于计算机视觉领域,为任意场景分类物体的探测。

最初选择过程涉及一个地区或感兴趣的对象。从那里使用,如Harris角检测器(HCD的),尺度不变特征变换(SIFT)或速成的强大功能(冲浪组功能仿射提取被视为表明仿射不变特征检测算法表示对象或地区。这一套有时被称为宏观功能或功能的星座。发现的功能的性质类型的对象或地区被列为它可能仍然是可能的匹配两个星座的特色,即便可能有轻微的差别如丢失或异常特征)两集。星座,而后说是功能分类设置。
哈希值是计算星座的特性这一般是由最初定义一个地方的哈希是为了居住空间中完成- 在这种状况下,散列值一个多层面的价值,定义的空间正常化。再加上计算的哈希值另外一个进程,决定了两个哈希值之间的距离是必要的过程-一个距离测量是必需的,而不是一个肯定性的平等经营者因为星座哈希值计算可能的差距问题也由于简单欧氏距离度量本质上是无效的,其结果是自动肯定特定空间的距离度量已成为学术界研究的活跃领域处理这类空间的非线性性质。
几何散列包括各类汽车分类的从新检测任意场景的目的,典型的例子。检测水平能够多种多样,从检测是不是车辆,到特定型号车辆,在特定的某个车辆。
4.布隆过滤器

布隆过滤器 容许 一个很是大范围内的值被一个小不少的内存锁表明 在计算机科学 ,这是 众所周知 的关联 查询 ,并在 关联容器 的核心理念。
Bloom Filter的 实现 经过 多种不一样的 hash函数 使用,也可 经过容许 一个 特定 值的存在 必定 的偏差 几率 会员 查询结果 的。 布隆过滤器 保证 提供的是 ,对于任何 会员国 的查询 就永远不会再 假阴性 ,但有 多是假 阳性。 假阳性 的几率能够 经过改变 控制 布隆过滤器 ,并 经过不一样的 hash函数的数量 所使用的 表的大小。
随后的研究 工做集中在 散列函数和哈希表以及 Mitzenmacher的布隆过滤器 领域。 建议 对这种 结构, 在数据 散列 最实用的用法 有助于 哈希 函数 熵, 理论成果 缔结一项 最佳的 布隆过滤器 (一个 提供 给定 一个 最低的 进一步 致使 假阳性 的可能性 表的大小 反之亦然 )提供 假阳性 的几率 定义 用户能够 建造 最多 也做为 两种大相径庭的 两两独立 的哈希 散列函数 已知功能 ,大大提升了 查询 效率 的成员
布隆过滤器 一般存在于 诸如 拼写 检查器 ,字符串匹配 算法, 网络数据包 分析工具和 网络/ Internet 缓存 的应用程序。
经常使用的哈希函数

通用的哈希函数库有下面这些混合了加法和一位操做的字符串哈希算法。下面的这些算法在用法和功能方面各有不一样,可是均可以做为学习哈希算法的实现的例子。(其余版本代码实现见下载

1.RS 
从Robert  Sedgwicks的  Algorithms in C一书中获得了。 我(原文做者)已经 添加了一些 简单的优化 算法,以 加快 散列 过程。
[java]  view plain copy
  1. public long RSHash(String str)  
  2.    {  
  3.       int b     = 378551;  
  4.       int a     = 63689;  
  5.       long hash = 0;  
  6.       for(int i = 0; i < str.length(); i++)  
  7.       {  
  8.          hash = hash * a + str.charAt(i);  
  9.          a    = a * b;  
  10.       }  
  11.       return hash;  
  12.    }  
 
2.JS
Justin Sobel写的一个位操做的哈希函数。
[c-sharp]  view plain copy
  1. public long JSHash(String str)  
  2.    {  
  3.       long hash = 1315423911;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2));  
  7.       }  
  8.       return hash;  
  9.    }  
 
3.PJW 
该散列 算法是基于贝尔实验室的 彼得J 温伯格 的的研究 。在Compilers一书中 (原则, 技术 和工具) 建议 采用这个算法的 散列 函数 的哈希 方法。
[java]  view plain copy
  1. public long PJWHash(String str)  
  2.    {  
  3.       long BitsInUnsignedInt = (long)(4 * 8);  
  4.       long ThreeQuarters     = (long)((BitsInUnsignedInt  * 3) / 4);  
  5.       long OneEighth         = (long)(BitsInUnsignedInt / 8);  
  6.       long HighBits          = (long)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);  
  7.       long hash              = 0;  
  8.       long test              = 0;  
  9.       for(int i = 0; i < str.length(); i++)  
  10.       {  
  11.          hash = (hash << OneEighth) + str.charAt(i);  
  12.          if((test = hash & HighBits)  != 0)  
  13.          {  
  14.             hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits));  
  15.          }  
  16.       }  
  17.       return hash;  
  18.    }  
 
4.ELF 
和PJW很类似,在Unix系统中使用的较多。
[java]  view plain copy
  1. public long ELFHash(String str)  
  2.    {  
  3.       long hash = 0;  
  4.       long x    = 0;  
  5.       for(int i = 0; i < str.length(); i++)  
  6.       {  
  7.          hash = (hash << 4) + str.charAt(i);  
  8.          if((x = hash & 0xF0000000L) != 0)  
  9.          {  
  10.             hash ^= (x >> 24);  
  11.          }  
  12.          hash &= ~x;  
  13.       }  
  14.       return hash;  
  15.    }  
 
5.BKDR
这个算法来自Brian Kernighan 和 Dennis Ritchie的 The C Programming Language。这是一个很简单的哈希算法,使用了一系列奇怪的数字,形式如31,3131,31...31,看上去和DJB算法很类似。(参照我以前一篇博客,这个就是Java的字符串哈希函数)
[java]  view plain copy
  1. public long BKDRHash(String str)  
  2.    {  
  3.       long seed = 131// 31 131 1313 13131 131313 etc..  
  4.       long hash = 0;  
  5.       for(int i = 0; i < str.length(); i++)  
  6.       {  
  7.          hash = (hash * seed) + str.charAt(i);  
  8.       }  
  9.       return hash;  
  10.    }  
 
6.SDBM
这个算法在开源的SDBM中使用,彷佛对不少不一样类型的数据都能获得不错的分布。
[java]  view plain copy
  1. public long SDBMHash(String str)  
  2.    {  
  3.       long hash = 0;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash = str.charAt(i) + (hash << 6) + (hash << 16) - hash;  
  7.       }  
  8.       return hash;  
  9.    }  
 
7.DJB
这个算法是Daniel J.Bernstein 教授发明的,是目前公布的最有效的哈希函数。
[java]  view plain copy
  1. public long DJBHash(String str)  
  2.    {  
  3.       long hash = 5381;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash = ((hash << 5) + hash) + str.charAt(i);  
  7.       }  
  8.       return hash;  
  9.    }  
 
8.DEK
由伟大的Knuth在《编程的艺术 第三卷》的第六章排序和搜索中给出。
[java]  view plain copy
  1. public long DEKHash(String str)  
  2.    {  
  3.       long hash = str.length();  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          hash = ((hash << 5) ^ (hash >> 27)) ^ str.charAt(i);  
  7.       }  
  8.       return hash;  
  9.    }  
 
9.AP
这是本文做者Arash Partow贡献的一个哈希函数,继承了上面以旋转觉得和加操做。代数描述: AP

[java]  view plain copy
  1. public long APHash(String str)  
  2.    {  
  3.       long hash = 0xAAAAAAAA;  
  4.       for(int i = 0; i < str.length(); i++)  
  5.       {  
  6.          if ((i & 1) == 0)  
  7.          {  
  8.             hash ^= ((hash << 7) ^ str.charAt(i) * (hash >> 3));  
  9.          }  
  10.          else  
  11.          {  
  12.             hash ^= (~((hash << 11) + str.charAt(i) ^ (hash >> 5)));  
  13.          }  
  14.       }  
  15.       return hash;  
  16.    }  
 

这里有一个关于这些算法的评测,能够稍微看看,本身也能够简单测试下,我在VSM试验中的测试,这些算法没有太大的性能差别,多是数据量较小的缘故。

 

 

各版本哈希代码下载

 

 

相关文章
相关标签/搜索