你好,我是悦创。

今天我们不聊爬虫,我们聊聊哈希算法,我算法其实一直都很烂,最近觉得得安排上日程了。毕竟好记性不如烂笔头嘛,所以就搞了这个篇当作算法的第一篇吧,写的有很多不足之处,不过我会继续努力的!

目录

  1. 和你聊聊哈希算法
  2. 什么是哈希函数?

1. 和你聊聊哈希算法

1.1 引言

哈希算法又称散列函数算法,是一种查找算法,应该说哈希算法是最快的查找算法,没有之一。对于查找问题,哈希算法一直是首选算法。那么,为什么名字起的这么“嘻哈”的算法会如此强大,本文将为你揭开谜底。

随着信息技术的迅猛发展,信息的重要性不言而喻,我们常常需要使用搜索引擎来解决日常生活中的大量问题,而搜索引擎中的一项关键技术就是数据查找算法。数据查找已经成为人们日常生活的一部分,因此,如何提高数据查找的效率是软件工程师不断研究的课题。

1.2 常见的查找算法

常见的数据查找算法一般可分为如下几种,如下图所示。

和你聊聊哈希算法_经验分享图片描述
  1. 顺序查找是最简单的查找方式,需要对数据集中的数据逐个进行匹配, 所以效率相对较低,不太适合大数据量的查找问题。
  2. 二分查找的查找效率很高,但是要求数据必须有序,而对数据排序通常需要更多的时间开销。
  3. 深度优先遍历、广度优先遍历对于大数据量的查找问题效率并不高。
  4. 哈希查找算法由于其查找速度快,查询、插入、删除操作简单等原因而获得了广泛的应用。很多问题本质上都是查找问题。解决查找问题,哈希算法是较好的选择。

1.3 哈希算法的工作原理

说了这么多,大家也肯定想知道哈希算法是怎么工作的,为什么这么快速?其实,哈希就像一本字典,当你需要查词的时候,通过目录找到页码,再到对应页码就能找到所需要的内容了。

艺术来源于生活,编程也一样来源于生活。在生活中,要想时刻能够找到自己的东西,最好的办法就是把东西放到固定的地方,每次需要它的时候就去相应的地方找,用完以后再放回原处。比如剪刀就放到阳台柜子的第二个抽屉,袜子就放到衣柜的第三层。

哈希算法也是一样的原理。简单来说,就是把一些复杂的数据,通过某种函数映射关系,映射成更加易于查找的方式。每个数据都会映射为独一无二的地址,数据存储时,它会存储于这个地址,取数据时,还会在这个地址取。使用哈希算法的数据结构叫做哈希表,也叫散列表 。

但是这种映射关系有可能会发生多个关键字映射到同一地址的现象,我们称之为冲突。在这种特殊情况下,需要对关键字进行第二次或更多次的处理,它通过把关键码值映射到表中一个唯一的位置来访问记录,以加快查找的速度。在大多数情况下,哈希算法可以实现在常数时间内存储和查找这些关键字。

1.4 哈希算法的其他应用

除以之外,哈希算法也在密码学中有着广泛的应用, 哈希算法用于消息摘要和签名,可以用于完整性校验。例如, 信息系统中最常见的登陆功能, 系统不会直接明文存储用户密码,而是把经过哈希算法映射后的值存储起来,当用户登陆时通过对比输入的密码的哈希值和数据库中的哈希值是不是一致。这样一来,即使黑客获取了登陆信息也无法知道用户的密码信息,从而提高了系统的安全性。

在选择哈希函数时,要考虑不同的应用场景,根据不同哈希函数的特点来选择。比如主要功能是存储和查询数据时,就应该多考虑查询的快速性,并且要尽量保证哈希值均匀分布。而在密码学中应用哈希算法时,就要优先考虑减少冲突,避免两个不同的明文的哈希值相同的情况发生。

2. 什么是哈希函数?

哈希算法进行查找的基本原理是根据数据量预先设置一个长度为 M 的数组,使用一个哈希函数 F 并以数据的关键字作为自变量,得到唯一的返回值,返回值的范围为 0 ~ M -1,这样就可以利用哈希函数 F 将数据元素映射到数组的某一位下标并把数据存放在对应位置上。查找时,利用哈希函数 F 计算该数据应该存储在哪里,再到相应的存储位置取出查找的数据。

哈希是一种高效查找算法,也是一种高效的存储算法。哈希算法是在时间和空间上作出权衡的经典例子。如果没有存储的限制,我们可以直接将关键字作为数组的索引,那么所有查找操作只需要访问内存一次即可完成。但这种理想情况只在数据量较小时可以应用,而当数据量很大时需要的内存太大。哈希算法是在空间和时间的开销之间找到了一种平衡。

设计一个好的哈希函数是使用哈希算法重中之重,好的哈希函数应该使得每个关键字都尽可能地散列到 M 个位置中的任何一个。

我们面对的第一个问题就是如何设计一个易于计算并且均匀分布所有的键的哈希函数,为了理解哈希函数,最好的方法就是去设计一个哈希函数。这一小节,我们将会学到除法哈希算法、乘法哈希算法、平方取中法、随机数哈希算法。

2.1 除法哈希算法

先来看第一个哈希函数:除法哈希,用所有的关键字去除一个特定的质数(选中的质数就是数组的长度),所得的余数就是该关键字的哈希值。通过 x(数据关键字) 除以 m(数组的长度) 的余数将关键字映射到数组的 m 个位置中。

质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

哈希函数公式为:h(x)=x mod m

例如,有一组数据3、11、8、6需要存储,选定的质数为 7 哈希函数为 F(x)=x%7,那么利用哈希函数 F 存储的数据如下图所示。

例如:3 对 7 取余结果为 3 ,则将 3 这个元素存放到数组 m[3] 的位置。

在选择 m 的值时,尽量选择质数,免得出现过多的多个关键字映射到一个位置的情况,降低哈希函数的效率。

按照统一的哈希函数存储数据,也按照统一的哈希函数查找数据,按照这种方法查找数据,理论上可以达到常数级别的查找效率。除法哈希函数速度相对较快,只需一次求余运算即可。

哈希算法和关键字的类型有关。最好的情况是,对于每种类型的关键字,我们都需要一个与之对应的哈希函数 。在上面的例子中关键字是数字,比如身份证号就可以直接使用这个哈希函数。但是实际的应用中会有很多不是数字的情况,那么我们应该找到一种将它们解释为数字的方法。例如,在做学生成绩的哈希映射时,把学生的姓名作为关键字,我们可以采用把名字的ASCII码相加的方式,就转换成了数字版本的关键字。

2.2 乘法哈希算法

对于给定的长度为 m 的数组,用关键字 x 乘以一个常数 N,N 的值为大于 0 并小于 1 的一个小数,并提取出 Nx 的小数部分。之后,用 m 乘以这个小数,再向下取整。

哈希函数公式为:h(x) = [m(Nx mod 1)] 其中 Nx mod 1 的意思就是 Nx 的小数部分。

相对于除法哈希算法,乘法哈希算法的优势是对于数组的长度 M 没有过多的要求,而对于常量 N 的选取,研究表明 N 的值为 0.618 较为不错。

2.3 平方取中法

平方取中法,是先计算出关键字的的平方值,然后取平方值中间几位作为哈希地址。

例如,关键字的集合为 {123,234,245},它们对应的平方值为{15129,54756,60025},如果我们选择平方值的百位和千位作为哈希值,则它们的哈希值为{51,47,0}。

2.4 随机数哈希算法

选择一个随机函数,以关键字作为的随机函数的种子,然后以随机函数的返回值作为该关键字的哈希值。通常情况下,当关键字的长度不等且不规则时采用这种方法。