`
wen866595
  • 浏览: 268977 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

散列/哈希

 
阅读更多

本文首先发表在: http://coderbee.net/index.php/algorithm/20130919/479 

 

散列一般也叫哈希。散列表也叫哈希表。本位将介绍散列表的基本知识、一致性哈希、哈希碰撞攻击及Java里的哈希实现。

 

介绍

散列表是普通数组概念的推广,在最坏情况下查找一个元素需要O(n),在一些合理假设下,查找一个元素的期望时间为O(1)。

 

在散列表中,不是直接把关键字用作数组下标,而是根据关键字计算出下标。

 

散列函数:作用就是根据关键字计算出数组下标。

 

碰撞(collision):多个关键字映射到同一个数组下标位置。

 

槽:一般把散列表的数组的一个存储单元(元素)叫做槽。

 

简单一致性散列:(Simple uniform hashing)假设任何元素散列到m个槽中的每一个的可能性是相同的,且与其他元素已被散列到什么位置上独立无关,这个假设称为简单一致性散列。

 

直接寻址表

当关键字的集合较小时,直接把对象放在表的槽中,节省空间;通常不需要存储对象的关键字域,因为如果有了一个对象在表中的下标,就可以得到它的关键字。由于没有存储关键字,就必须有某种方法来确定某个槽是否为空。

 

在直接寻址的方式下,具有关键字k的元素被存放在槽k中。

 

散列表

在散列表中,具有关键字k的元素存放h(k)中,也就是通过散列函数h来确定存储的槽位。

 

使用散列函数的问题是两个关键字可能映射到同一个槽上,也就是发生碰撞。通过精心设计的随机散列函数可以尽量减少碰撞。

 

通过链接法解决碰撞

在链接法(chaning)中,把散列到同一槽中的所有元素都放在一个链表中。

hash_collision_chaining

从图可知,插入和搜索的时间与链表的长度成正比。

 

查找时,首先要计算出关键字对应的槽,然后遍历链表匹配元素。

 

散列函数

一个好的散列函数应(近似地)满足简单一致性散列的假设。一种好的做法是以独立于数据中可能存在的任何模式的方式导出散列值。

 

除法散列法

通过取关键字k除以m的余数,来将关键字k映射到m个槽的某一个去。散列函数为:h(k) = k mod m。m一般取与2的整数幂不太接近的质数。

 

乘法散列法

用关键字k乘上常数A(0<A<1),并抽取出kA的小数部分,用m乘以这个值,再取结果的底(floor)。散列函数为: h(k) = floor(m * (k*A mod 1))

 

全域散列

universal hashing:随机地选择散列函数,使之独立于要存储的关键字。

 

全域散列的基本思想是在执行开始时,就从一族仔细设计的函数中,随机地选择一个作为散列函数。

 

开放寻址法

在开放寻址法(open addressing)中,所有的元素都存放在散列表里。即每个表项或包含动态集合的一个元素,或包含NIL。当查找一个元素时,要检查所有的表项,直到找到所需的元素或者最终发现元素不在表中。

不像在链接法中,这里没有链表,也没有元素存放在散列表外。

 

在开放寻址法中,当要插入一个元素时,可以连续地检查(或称探查)散列表的各项,直到找到一个空槽来防止待插入的关键字为止。如果需要,会检查所有的槽。

 

探查方法一般有三种:线性探查、二次探查、双重探查。

 

一致性哈希

假如有N台cache 服务器(简称 cache),那么如何将一个对象object映射到N个cache上呢?很可能会采用类似下面的通用方法计算object的hash值,然后均匀的映射到到N个cache ;hash(object)%N

 

这样的话,如果有一台cache当机,映射关系变为:hash(object)%(N-1);新增一台cache时,映射关系变为:hash(object)%(N+1)。这两种情况都会导致对象被映射到不同的缓存,从而导致缓存失效。一致性哈希就是解决这类场景的。

 

单调性

单调性( Monotonicity ):是指如果已经有一些内容通过哈希映射到相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

 

consistent hashing 是一种 hash 算法,简单的说,在移除 / 添加一个 cache 时,它能够尽可能小的改变已存在 key 映射关系,尽可能的满足单调性的要求。

 

假设有一个哈希空间,一致性哈希需要把对象和cache映射到这个缓存空间去。 consistent_hashing

圆圈表示哈希空间,是固定的,独立于cache 和 对象;蓝色的结点表示cache;红色的结点表示对象。

 

一致性哈希算法的巧妙之处在于:对于一个对象object,通过给定的哈希算法得到它的的哈希值hashCode,这个hashCode对应在哈希 空间的一个点,但这个hashCode不直接对应一个cache;为了找到这个hashCode对应的cache,需要按一种固定的顺序(比如顺时针)在 哈希空间上查找它cache,查找到的第一个cache即为这个对象所对应的cache。相当于二次散列。

 

要注意的是:通过hashCode在哈希空间查找cache时,找到的第一个cache就是对象所对应的cache,不会查找后续的cache,这就确保了一致性哈希算法具有访问局部性的特性。

 

假如哈希空间上的查找顺序为顺时针的:

  • 当新增一个cache D时,它映射到key2和key3之间,那么受影响的对象只局限在:cache D和它的上一个cache结点(即cache B) 之间的对象,这些对象会被映射到新cache上去;cache B之前和cache D之后的对象都不会受影响。

  • 当移除一个ceche B时,B和A之间的对象会被映射到B的下一个结点C上去。B和C之间的及A之前的对象也不会受影响。

更多详细介绍可参考:一致性hash算法 – consistent hashing

 

哈希碰撞攻击

哈希碰撞攻击是指:对于使用链接法解决碰撞的散列表实现,通过精心构建一组对象,使这些对象映射到同一个槽位,从而使散列表退化为一个链表,由于链表的操作效率平均比散列表低,从而导致散列表的性能下降,进而让整个应用程序的性能以级数下降。

 

关于通过哈希碰撞攻击的可以参考这篇文章:Hash Collision DoS 问题

 

Java里的散列

java.util.HashMap类是JDK里的散列表实现,它的实现方式是槽+链表,也就是用链接法解决碰撞。

它的散列算法是除法散列的思想,与大多数教科书里讲的不同是,它的槽的长度是2的整数次幂,而不是一个质数;且用按位与运算代替除法:

//  计算给定哈希值h所对应的槽下标
static int indexFor(int h, int length) {
    // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
    return h & (length-1);
}

h是对象的哈希值,length是槽的长度。这样做主要是出于性能的考虑。

 

对于哈希碰撞攻击,java.util.HashMap类从JDK1.8开始会在链表的长度达到某个阀值时,将散列表的实现转换为一个定制的红黑树。

 

如果对了解更多,当然应该通过google去查找更多资料。

分享到:
评论

相关推荐

    SHA256 摘要算法 、HMAC_SHA256 散列/哈希算法 C语言实现,适应于各种嵌入式单片机

    void sha256_get(uint8_t hash[32], const uint8_t *message, int length);/*此函数用于对消息计算摘要值,输入任意大小消息,输出32字节摘要值*/ void hmac_sha256_get(uint8_t digest[32], uint8_t *message, int...

    散列/哈希 sha256.js

    var sha256= SHA256("要加密的值"); /* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ 文件来源:...

    哈希表线性探测再散列(纯数字)

    c代码实现哈希表线性探测再散列。关键字均为纯数字。查找时为单次查找,未加入循环

    MD5散列算法/MD5哈希

    它采用了一种单向哈希函数的方式,将任意长度的数据转换为一个固定长度的、独特的哈希值。这段代码的核心功能是将输入的字符串或数据块进行MD5哈希计算,生成一个32位的十六进制字符串作为输出。由于MD5算法的广泛...

    各种加密解密在线演示

    文字在线加密解密、散列/哈希、BASE64、SHA1、SHA224、SHA256、SHA384、SHA512、MD5、HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512、HmacMD5、urlencode、urldecode

    tinyToolkit:tinyToolkit是为减少编码工作而封装的简易工具套件,可单独提取二进制嵌入项目代码中,最低支持c ++ 11

    hash(散列/哈希处理) md5 sha1 sha224 sha256 sha384 sha512 hmac_md5 hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512 池化处理 taskPool任务(线程)池 objectPool对象池 util(实用...

    哈希函数的应用(数据结构课程设计)

    哈希表可以使用线性探测再散列解决冲突,以提高查找效率。 哈希函数的优缺 哈希函数的优点包括: * 高效的查找速度 * 高效的存储空间 * 可以用于各种应用 而哈希函数的缺点包括: * 可能会出现冲突 * 需要选择...

    散列表 (哈希表,线性探测再散列)

    ### 散列表(哈希表,线性探测再散列) #### 1. 散列表概念 散列表,又称哈希表,是一种基于数组结构的数据结构。它通过哈希函数将一组关键字映射到一个有限的连续地址集(即数组索引范围),并将这些关键字作为...

    java图像压缩源码-bookmark:一个java程序员日常书签导航~在线工具,API,帮助手册,资源,阅读

    :文本加解密,MD5/base64/散列/哈希 :unix时间戳(10位整数)转换 :sql代码格式化 :根据输入的sql生成java代码 :json字符串格式化 :json在线编辑器 :javascript正则在线测试 :文本编码转换 :在线运行代码java、...

    大数据处理方法

    #### 二、Hashing(散列/哈希) **适用范围:** 哈希表用于快速查找和删除,适用于数据量适中且能放入内存的情况。 **基本原理及要点:** - **哈希函数选择:** 针对不同类型的数据(如字符串、整数等),选择合适...

    用二次探测再散列法解决冲突建立哈希表并查找

    二次探测再散列法是一种解决哈希冲突的方法,当一个元素的哈希地址已经被其他元素占用时,我们不会立即跳到下一个地址,而是按照平方序列(如 1, 4, 9, ...)进行探测。在本例中,如果初始哈希地址是 `i`,冲突发生...

    C/C++哈希表实现电话号码查询系统

    哈希函数是将任意长度的输入(通常称为键或关键字)转换为固定长度的输出,这个输出通常称为哈希值或散列。一个好的哈希函数应该能均匀地分布输入,避免冲突,即不同的输入得到相同的哈希值。在电话号码查询系统中,...

    C++哈希模板(基于邻接表) 散列

    在IT领域,哈希表是一种高效的数据结构,它利用散列函数将数据映射到一个固定大小的数组中,以实现快速的查找、插入和删除操作。哈希表的性能通常远优于线性搜索,其平均时间复杂度可以达到O(1)。本程序是基于C++...

    哈希查找数据结构实验报告.pdf

    在该部分中,我们详细介绍了哈希表的实现细节,包括元素类型、结点类型、全局变量、哈希函数、二次探测再散列解决冲突、创建哈希表、查找哈希表等方面。 哈希函数的实现使用除留余数法,二次探测再散列解决冲突使用...

    散列查找算法_哈希表

    程序先调用自己写的Create函数创建一个长度为13的哈希表,原始数据是:{10,9,8,7,5,4,6,3,2,1,95},长度为11,这个程序使用的是除留余数法,构建的哈希表为:{0,1,2,3,4,5,6,7,8,9,10,95,0}. 然后再调用Haxi_Sou...

    什么是Hash

    Hash也称散列、哈希。基本原理就是把任意长度的输入,通过Hash算法变成固定长度的输出。这个映射的规则就是Hash算法,而原始数据映射后的二进制串就是哈希值。 函数把消息或数据压缩成摘要,使得数据量变小,将数据...

    linux下C实现的哈希表

    else //不可以直接插入,需要再散列 { int num=KEY(data); printf("num=%d\n",data); int i=1; do { num=(num+i*i); i++; }while(((*hp)-&gt;pNode[num]).data!=8888); ((*hp)-&gt;pNode[num]).data=data...

    cpp代码-二次探测再散列哈希表

    在C++中实现二次探测再散列哈希表,首先我们需要一个动态数组来存储元素。这个数组通常被称为哈希表,而哈希函数是将键映射到数组索引的关键部分。为了处理冲突,我们需要定义一个探测序列,比如i, i^2, 2i^2, ...,...

    学生管理哈希表的实现算法

    哈希表是一种高效的数据结构,它通过特定的算法——哈希函数,将任意大小的键(key)映射到一个固定大小的数组中,从而实现快速查找、插入和删除操作。在学生管理的场景中,我们可以用哈希表来存储学生的信息,例如...

Global site tag (gtag.js) - Google Analytics