首页 > 编程语言 >一些好玩的Hash算法(CMU15445)

一些好玩的Hash算法(CMU15445)

时间:2023-12-12 22:34:08浏览次数:28  
标签:Hash key Bucket CMU15445 链表 哈希 好玩 Hashing

graph LR R[HashTable] --> St[静态哈希策略] R --> Dy[动态哈希策略] St --> 线性探测法 St --> t1[Robin Hood] St --> t2[Cuckoo Hashing] Dy --> Ch[Chained Hashing] Dy --> Ex[Extendible Hashing] Dy --> Lin[Linear Hashing]

Hash策略的分类

静态哈希

哈希表底层实际是一个数组嘛,静态哈希就是一个槽位一个值,当碰撞发生会采用某种机制占用其它的槽位。

那么假设当前槽位数为N,就只能存N个元素,如果想要保存更多元素,需要创建新的底层数组,对Hash表上的全部元素重新使用新的Hash函数进行迁移。

典型的静态哈希算法就是上课都学过的线性探测法(也叫开放寻址法)。

所以使用静态哈希的前提是你能够较为准确的预估要保存的键值对的数量,然后你提供更多个槽位,避免哈希表迁移。但在数据库系统中,我们经常使用Hash表来做Join之类的工作,在Join之前,我们很难预估具体的数量。

动态哈希

为了避免静态哈希的迁移,动态哈希策略允许哈希表中存储大于槽位个键值对。

若当前槽位数为N,动态哈希策略允许保存多于N个元素

最简单的就是链表法嘛(但是数据库系统中的稍有些差别),动态哈希算法通常还要考虑在元素数量不断增加时如何渐进式的改动整个Hash表,从而让性能不会退化的太严重(考虑链表法性能会下降到O(N))

Chained Hashing

img

实际上和链表法是一样的。

首先我们有N个槽位,每一个槽位指向一个链表,这样我们便可以无限的向N个槽的HashTable添加更多的内容。

有一个区别,在数据库系统中应用的HashTable,通常都会有Bucket的概念,比如这里槽中保存的实际上是一个Bucket的链表。

Bucket中有多条键值对数据,正常来说它应该是一个数据库页的大小,我想这样做的原因有两个:

  1. 更利好磁盘,每次从磁盘读20字节挺蠢的
  2. 利好CPU的CacheLine,因为相邻的数据被放置在Bucket中,CPU一次性预取到缓存内。考虑Bucket大小为一个键值对(也就是单纯的链表),那么遍历是随机的,每一个键值对都要访问一次内存

链地址法的问题挺明显的,就是它虽然允许无限存放数据,但当数据量M与N的差值越大,对HashTable的访问性能趋近于链表

实际上可以将Bucket链表组织成树以加速查找,但是教授指出实际的商用系统里可能还是更偏向直接使用简单的链表

若想让HashTable的访问性能恢复,实际上你还得重建整个HashTable,对每一个元素进行重映射,而这正与我们使用动态策略的目标相悖。所以,后面的这些策略非常精妙,它们都允许渐进式的对Hash表进行扩展,并且避免在扩展时进行全局的重映射。

Extendiable Hashing

我这里斗胆来描述一下这个策略。

  • 我们有一个全局变量 G ,Hash表有 2^G 个槽位
  • 每个槽位指向一个Bucket,Bucket有一个局部变量 L
  • 我们使用 hash(key) 的前 G 位来确定一个key所在的槽位
  • 该槽位指向的Bucket中,若对其key进行 hash(key) ,得到的结果前 L 位都是相同的
  • 我们假设一个Bucket能存3条数据
  • 这里你脑子里需要注意的是,我们为了方便你观察,将Bucket中的entry写成了key的hash,实际上这里存的是实际的key-value数据,稍后在对一个Bucket中的元素进行转移时,我们必须将所有的key都重新做 hash(key) 运算。这种Bucket级别的迁移要比整表迁移好多了。

img

现在执行 insert(k)hash(k) = 1001110 ,明显,它要定位到第二个槽位,此时第二个槽位指向的Bucket已经满了。

此时如果我们加大前缀判断的位数,我们就可以将这个Bucket拆散,其中第一条,第三条中第二位为1,它们会分到一个Bucket中,第二条和新插入的数据第二位为0,它们会被分到一个Bucket中。

但由于 L==G ,L已经达到G的大小,G也必须跟着扩大,L和G都等于2,所以Hash表会被扩容成这样:

img

  • 原来的溢出Bucket被拆分成了两个,并且L都增加了
  • Hash表的槽位也被扩容了,但实际上只是对数组的简单复制,并不涉及到对所有key的rehash
  • 未溢出的Bucket没动,00开头的和01开头的槽位都指向它,而它的 L==1 ,满足前1位相等的要求,前一位都是0

考虑此时插入 hash(k)=0100101 ,那么第一个Bucket会发生溢出,由于 L<G ,说明有多个槽位指向该Bucket,那么不用扩容数组。

img

考虑若再次插入 hash(k)=0110001 ,此时第三个Bucket又将溢出,而 L==G ,再次对数组扩容:

img

我们发现我们进行重映射的还是只有两个Bucket(我们把对应的Bucket和指向它们的指针标成了红色),其它坑位的性质不受任何影响

Extendiable Hashing让我们可以扩大底层数组的规模而不用重映射所有的元素,只重映射一个Bucket即可。

但其缺点也是每次扩大底层数组时要以2倍的形式扩充,并且要对底层数组做修复(将指针指向正确的Bucket),在修复完成前,该数据结构必须被latch锁住以防并发错误。

Linear Hashing

Linear Hashing每次只将底层数组扩大一个元素。

标签:Hash,key,Bucket,CMU15445,链表,哈希,好玩,Hashing
From: https://www.cnblogs.com/lilpig/p/17897992.html

相关文章

  • #yyds干货盘点#History 与 hash
    url组成//http://127.0.0.1:8001/01-hash.html?a=100&b=20#/aaa/bbblocation.protocal//'http:'协议localtion.hostname//'127.0.0.1'主机名location.host//'127.0.0.1:8001'主机location.port//8001端口号location.pathname//'......
  • 【Java集合】双列集合HashMap的概念、特点及使用
    上篇文章讲了Map接口的概念,通过他提供的接口方法,我们学习了如何使用以及对Map集合的遍历HashMap概念HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,该集合的键和值允许为空,但键不能重复,且集合中的元素是无序的。特点HashMap底层是由哈希表结构组成的,其实就是“数组......
  • day18 hash logging模块
    day182023年12月9日周六14:03:43day17复习datetime.datetime.now()要什么文件切割就可以random.choice([1,2,3])随机选择random.shuffle()打乱顺序random.random(1,2)随机取数os.mkdir()新建一个文件夹os模块与操作系统交互操作文件和文件夹sys与py解释器交互环境变量......
  • 【JavaSE】集合Collection{List(ArrayList, LinkedList), Set(TreeSet, HashSet, Link
    集合单列集合:Collection接口单列集合:一次添加一个元素;如果集合中添加的是类,要重写equals方法,否则比较的是地址,无法正常删除内容相同的元素。单列集合通用遍历方式1.迭代器遍历2.增强for循环遍历增强for循环底层逻辑还是迭代器,字节码文件反编译为java会发现还是迭代......
  • 【JavaSE】数据结构-哈希表(HashSet/HashMap底层哈希表详解,源码分析)
    哈希表结构JDK8版本之前:数组+链表JDK8版本及之后:数组+链表+红黑树哈希表HashMapput()方法的添加流程创建HashSet集合时,构造方法中自动创建HashMap集合;HashMap空参构造方法会创建一个默认长度为16,默认加载因子为0.75的数组,数组名为table(tips:实际上,HashSet对象创建后,第......
  • Python的hashlib模块
    一、什么是摘要算法1、摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)用于生成数据或文本的简短摘要或哈希值的算法。它们被广泛应用于密码学、数据完整性验证和信息检索等领域。摘要算法通过对输入数据进......
  • Go - two bcrypt hashes of the same password are NOT equal
     packagemainimport("fmt""golang.org/x/crypto/bcrypt")funcmain(){password:="abcdef"hashedPassword1,_:=bcrypt.GenerateFromPassword([]byte(password),bcrypt.DefaultCost)fmt.Println(strin......
  • 数据结构 玩转数据结构 14-3 java中的hashCode方法
    0课程地址https://coding.imooc.com/lesson/207.html#mid=15346 1重点关注1.1重写hashCode和equals方法参见3.1  2课程内容2.1不同的对象的默认hashCode方法Integer相同数字的一样Double相同数字的一样String......
  • 你真的了解HashSet 和HashMap的区别、优缺点、使用场景吗?
     HashSet和HashMap是Java集合框架中的两个常用类,它们都用于存储和管理数据,但在使用方式、功能和性能上有很大的区别。HashSet和HashMap的区别区别一:用途不同HashSet: HashSet是一个基于哈希表的集合,用于存储不重复的元素,它不存储键值对。它实际上是基于HashMap......
  • HashMap超详细源码解析
    原文链接:HashMap和HashSet源码解析1、HashMap概念HashMap实现了Map接口,是一种使用键值对存储数据的数据结构。HashMap允许null作为键和值。HashMap不保证元素的顺序,特别是不保证顺序恒定。HashMap是基于哈希表实现的数据结构,具有快速的插入、删除和查找操作。HashMap使用......