文章出处:http://hunteagle.javaeye.com
注:最近因为在做和hash有关的题目,感到很纠结。虽然上学期数据结构学过,但是当时觉得hash没什么用,所以没有认真学~后悔啊~~~现在恶补一下~
计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数据。用“人 类”的语言描述单向函数就是:如果某个函数在给定输入的时候,很容易计算出其结果来;而当给定结果的时候,很难计算出输入来,这就是单项函数。各种加密函 数都可以被认为是单向函数的逼近。Hash函数(或者成为散列函数)也可以看成是单向函数的一个逼近。即它接近于满足单向函数的定义。
Hash函数还有另外的含义。实际中的Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。除此以外,Hash函数往往应用于查找上。所以,在考虑使用Hash函数之前,需要明白它的几个限制:
1. Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
2. 由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
3. 不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。
应用于加密的Hash函数已经探讨过太多了,在作者的博客里面有更详细的介绍。所以,本文只探讨用于查找的Hash函数。
Hash函数应用的主要对象是数组(比如,字符串),而其目标一般是一个int类型。以下我们都按照这种方式来说明。
一般的说,Hash函数可以简单的划分为如下几类:
1. 加法Hash;
2. 位运算Hash;
3. 乘法Hash;
4. 除法Hash;
5. 查表Hash;
6. 混合Hash;
下面详细的介绍以上各种方式在实际中的运用。
一 加法Hash
所谓的加法Hash就是把输入元素一个一个的加起来构成最后的结果。标准的加法Hash的构造如下:
static int additiveHash(String key, int prime)
{
int hash, i;
for (hash = key.length(), i = 0; i < key.length(); i++)
hash += key.charAt(i);
return (hash % prime);
}
这里的prime是任意的质数,看得出,结果的值域为[0,prime-1]。
二 位运算Hash
这类型Hash函数通过利用各种位运算(常见的是移位和异或)来充分的混合输入元素。比如,标准的旋转Hash的构造如下:
static int rotatingHash(String key, int prime)
{
int hash, i;
for (hash=key.length(), i=0; i<key.length(); ++i)
hash = (hash<<4)^(hash>>28)^key.charAt(i);
return (hash % prime);
}
先移位,然后再进行各种位运算是这种类型Hash函数的主要特点。比如,以上的那段计算hash的代码还可以有如下几种变形:
1. hash = (hash<<5)^(hash>>27)^key.charAt(i);
2. hash += key.charAt(i);
hash += (hash << 10);
hash ^= (hash >> 6);
3. if((i&1) == 0)
{
hash ^= (hash<<7) ^ key.charAt(i) ^ (hash>>3);
}
else
{
hash ^= ~((hash<<11) ^ key.charAt(i) ^ (hash >>5));
}
4. hash += (hash<<5) + key.charAt(i);
5. hash = key.charAt(i) + (hash<<6) + (hash>>16) – hash;
6. hash ^= ((hash<<5) + key.charAt(i) + (hash>>2));
三 乘法Hash
这种类型的Hash函数利用了乘法的不相关性(乘法的这种性质,最有名的莫过于平方取头尾的随机数生成算法,虽然这种算法效果并不好)。比如,
static int bernstein(String key)
{
int hash = 0;
int i;
for (i=0; i<key.length(); ++i) hash = 33*hash + key.charAt(i);
return hash;
}
jdk5.0里面的String类的hashCode()方法也使用乘法Hash。不过,它使用的乘数是31。推荐的乘数还有:131, 1313, 13131, 131313等等。
使用这种方式的著名Hash函数还有:
// 32位FNV算法
int M_SHIFT = 0;
public int FNVHash(byte[] data)
{
int hash = (int)2166136261L;
for(byte b : data)
hash = (hash * 16777619) ^ b;
if (M_SHIFT == 0)
return hash;
return (hash ^ (hash >> M_SHIFT)) & M_MASK;
}
以及改进的FNV算法:
public static int FNVHash1(String data)
{
final int p = 16777619;
int hash = (int)2166136261L;
for(int i=0;i<data.length();i++)
hash = (hash ^ data.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return hash;
}
除了乘以一个固定的数,常见的还有乘以一个不断改变的数,比如:
static int RSHash(String str)
{
int b = 378551;
int a = 63689;
int hash = 0;
for(int i = 0; i < str.length(); i++)
{
hash = hash * a + str.charAt(i);
a = a * b;
}
return (hash & 0x7FFFFFFF);
}
虽然Adler32算法的应用没有CRC32广泛,不过,它可能是乘法Hash里面最有名的一个了。关于它的介绍,大家可以去看RFC 1950规范。
四 除法Hash
除法和乘法一样,同样具有表面上看起来的不相关性。不过,因为除法太慢,这种方式几乎找不到真正的应用。需要注意的是,我们在前面看到的hash的 结果除以一个prime的目的只是为了保证结果的范围。如果你不需要它限制一个范围的话,可以使用如下的代码替代”hash%prime”: hash = hash ^ (hash>>10) ^ (hash>>20)。
五 查表Hash
查表Hash最有名的例子莫过于CRC系列算法。虽然CRC系列算法本身并不是查表,但是,查表是它的一种最快的实现方式。查表Hash中有名的例子有:Universal Hashing和Zobrist Hashing。他们的表格都是随机生成的。
六 混合Hash
混合Hash算法利用了以上各种方式。各种常见的Hash算法,比如MD5、Tiger都属于这个范围。它们一般很少在面向查找的Hash函数里面使用。
七 对Hash算法的评价
http://www.burtleburtle.net/bob/hash/doobs.html 这个页面提供了对几种流行Hash算法的评价。我们对Hash函数的建议如下:
1. 字符串的Hash。最简单可以使用基本的乘法Hash,当乘数为33时,对于英文单词有很好的散列效果(小于6个的小写形式可以保证没有冲突)。复杂一点可以使用FNV算法(及其改进形式),它对于比较长的字符串,在速度和效果上都不错。
2. 长数组的Hash。可以使用http://burtleburtle.net/bob/c/lookup3.c这种算法,它一次运算多个字节,速度还算不错。
八 后记
本文简略的介绍了一番实际应用中的用于查找的Hash算法。Hash算法除了应用于这个方面以外,另外一个著名的应用是巨型字符串匹配(这时的 Hash算法叫做:rolling hash,因为它必须可以滚动的计算)。设计一个真正好的Hash算法并不是一件容易的事情。做为应用来说,选择一个适合的算法是最重要的。
九 数组hash
inline int hashcode(const int *v)
{
int s = 0;
for(int i=0; i<k; i++)
s=((s<<2)+(v[i]>>4))^(v[i]<<10);
s = s % M;
s = s < 0 ? s + M : s;
return s;
}
注:虽说以上的hash能极大程度地避免冲突,但是冲突是在所难免的。所以无论用哪种hash函数,都要加上处理冲突的方法。
相关推荐
### 经典Hash算法概述与实现 #### 一、引言 哈希算法在计算机科学领域扮演着极其重要的角色,特别是在数据检索、信息安全以及数据完整性校验等方面。它能够将任意长度的数据转换成一个固定长度的哈希值,这一过程在...
Java实现GeoHash算法是一种在IT领域中用于地理位置数据存储和检索的技术。GeoHash将经纬度坐标转换为字符串,使得地理位置可以被高效地索引和查询。这种算法利用了空间分割和编码策略,使得相邻的位置在编码后具有...
在IT领域,哈希算法(Hash Algorithm)是一种用于将任意长度的数据转化为固定长度输出的算法。这个过程通常称为哈希或散列。哈希算法在信息安全、数据完整性验证、密码学等多个方面都有着广泛的应用。本项目是用...
本篇文章将详细讨论几种常见的哈希算法及其在Java中的实现。 1. **MD5(Message-Digest Algorithm 5)** MD5是一种广泛使用的哈希函数,产生128位(16字节)的哈希值,通常表示为32个十六进制数字。尽管MD5在安全...
### Hash算法相关介绍 在计算机科学领域,哈希(Hash)是一种将任意长度的数据映射为固定长度数据的技术。哈希算法广泛应用于多种场景中,包括但不限于数据完整性验证、密码存储、快速查找等。本文主要介绍了几种...
GeoHash算法是一种基于地理坐标的分布式空间索引技术,它通过将地球表面的经纬度坐标转化为可比较的字符串,使得我们可以高效地进行地理位置的搜索、范围查询以及邻居查找等操作。这种算法尤其适用于大数据和分布式...
在本文中,我们将探讨几种经典哈希算法的实现,包括PHP、OpenSSL和MySQL中的字符串哈希函数。 1. PHP中的哈希函数(hashpjw) PHP使用了一个名为hashpjw的函数,由Peter J. Weinberger设计。该函数采用移位和异或...
Hash算法是一种在计算机科学中广泛使用的数据结构和算法,它通过特定的函数将任意长度的输入(也称为键或关键字)映射为固定长度的输出,这个输出通常称为哈希值。这种映射过程旨在快速查找和访问数据,因为哈希函数...
标题和描述提到的“几种常见的hash算法1”主要是针对哈希算法在不同应用中的使用及其特性进行的探讨。 首先,哈希算法的理论基础是单向函数,它是一种在给定输入时容易计算输出,但在给定输出时难以反推输入的函数...
本文档介绍了几种常见的Hash算法及其应用场景。其中特别推荐了FNV1算法,该算法不仅计算效率高,而且具有良好的散列质量,非常适合在网络通信等场景中使用。对于不同的应用场景,可以根据实际需求选择合适的Hash算法...
一致性哈希算法是一种分布式哈希(Distributed Hash Table, DHT)技术,旨在解决在分布式环境中数据分布不均匀的问题。Ketama算法是基于一致性哈希的一种优化实现,由Last.fm公司的Simon Willison提出,其目标是在...
以下介绍几种常见的经典哈希算法: 1. **RS哈希(Robert Sedgewick哈希)** RS哈希算法由Robert Sedgewick提出,其基本思想是利用乘法和加法运算来构建哈希值。在给出的代码中,`RSHash`函数通过将字符乘以累乘...
内存页面调度是操作系统中至关重要的一个环节,它的主要目标是有效地管理和使用有限的物理内存资源...通过对比不同算法的命中率,可以直观地看出每种算法在处理页面调度时的效率差异,从而更好地理解它们各自的优缺点。
SHA-1是由美国国家安全局(NSA)设计,并由国家标准和技术研究院(NIST)发布的一种Hash算法标准。它产生的Hash值长度为160位(20字节),并且具有较高的安全性。然而,近年来的研究表明,SHA-1已经不再被视为足够...
Hash算法是一种在计算机科学中广泛应用的数据处理技术,主要用于将输入数据(通常称为“键”)转换为固定长度的输出(通常称为“哈希值”或“散列值”)。这种转换过程是由特定的函数完成的,即哈希函数。哈希函数的...
Hash算法在IT行业中扮演着至关重要的角色,尤其是在信息安全和数据完整性验证方面。本实验主题为“Hash算法实验”,主要涉及的是密码学中的消息摘要技术,具体是使用MD5(Message-Digest Algorithm 5)算法对文件...
总的来说,Hash算法与稀疏矩阵的结合在个性化推荐系统中是一种有效的解决方案,它能有效地处理高稀疏性的用户-项目矩阵,提高数据处理效率,从而提升推荐系统的性能。通过C#编程语言实现,我们可以利用.NET ...
标题中的"SPlayer视频文件hash算法"指的是射手播放器(SPlayer)用于识别和验证视频文件的一种特定技术。这种算法能够生成一个唯一哈希值(hash value),也称为特征码,来表示文件的内容。哈希算法在信息技术中广泛...
SHA1(Secure Hash Algorithm 1)是一种广泛使用的散列函数,属于哈希算法的一种,它能够将任意长度的输入(也叫做预映射)通过一个单向函数转换为固定长度的输出,通常这个长度是160位。SHA1算法在网络安全、数据...