哈希函数广泛应用于计算机科学和密码学中,具有多种用途,包括数据完整性、数字签名、密码存储等。
哈希函数有很多种类型,每种都有自己的优点和缺点。以下是一些最常见的类型:
1. SHA(安全哈希算法):SHA是由美国国家安全局(NSA)设计的一系列加密哈希函数。最广泛使用的 SHA 算法是 SHA-1、SHA-2 和 SHA-3。以下是每项的简要概述:
2. CRC(循环冗余校验) :CRC是一种非加密散列函数,主要用于数据传输中的错误检测。它快速且高效,但不适合安全目的。CRC 背后的基本思想是将固定长度的校验值或校验和附加到消息的末尾。该校验和是根据消息的内容使用数学算法计算出来的,然后与消息一起传输。
当接收到消息时,接收方可以使用相同的算法重新计算校验和,并将其与随消息发送的校验和进行比较。如果两个校验和匹配,接收方可以合理地确定消息在传输过程中没有损坏。
用于 CRC 的具体算法取决于应用和所需的错误检测级别。一些常见的 CRC 算法包括 CRC-16、CRC-32 和 CRC-CCITT。
3. MurmurHash:MurmurHash 是一种快速高效的非加密哈希函数,设计用于哈希表和其他数据结构。它不适合安全目的,因为它容易受到碰撞攻击。
4. BLAKE2:BLAKE2 是一种加密哈希函数,旨在快速且安全。它是对流行的 SHA-3 算法的改进,广泛应用于需要高速哈希的应用程序,例如加密货币挖掘。
BLAKE2 有两个版本:BLAKE2b 和 BLAKE2s。BLAKE2b 针对 64 位平台进行了优化,可生成高达 512 位的哈希值,而 BLAKE2s 针对 8 至 32 位平台进行了优化,可生成高达 256 位的哈希值。
5. Argon2:Argon2 是一种内存硬密码哈希函数,旨在抵抗暴力攻击。它广泛用于密码存储,并受到密码哈希竞赛推荐。Argon2的主要目标是让攻击者难以使用暴力攻击或字典攻击等技术破解密码。它通过使用计算密集型算法来实现这一点,使攻击者很难在短时间内进行大量密码猜测。
Argon2 具有几个关键功能,使其成为密码散列和密钥派生的有力选择:
抵御旁道攻击:Argon2 旨在抵御旁道攻击,例如定时攻击或功率分析攻击,这些攻击可用于提取有关被散列的密码的信息。
6. MD5(消息摘要5) :MD5是一种广泛使用的加密哈希函数,可产生128位哈希值。它快速且高效,但由于已知的漏洞,出于安全目的不再推荐使用它。MD5 背后的基本思想是获取任意长度的输入消息,并生成固定长度的输出,称为哈希值或消息摘要。该哈希值对于输入消息来说是唯一的,并且是使用涉及一系列逻辑运算的数学算法生成的,例如按位运算、模运算和逻辑函数。
MD5 广泛应用于各种应用,包括数字签名、密码存储和数据完整性检查。然而,它已被证明存在使其容易受到攻击的弱点。特别是,有可能生成具有相同 MD5 哈希值的两条不同消息,这种漏洞称为冲突攻击。
还有许多其他类型的哈希函数,每种都有其独特的功能和应用。哈希函数的选择取决于应用程序的具体要求,例如速度、安全性和内存使用情况。
将每个项目映射到其自己的唯一槽的哈希函数称为完美哈希函数。如果我们知道项目并且集合永远不会改变,我们就可以构造一个完美的哈希函数,但问题是,给定任意项目集合,没有系统的方法来构造完美的哈希函数。幸运的是,即使哈希函数并不完美,我们仍然可以获得性能效率。我们可以通过增加哈希表的大小来实现完美的哈希函数,以便可以容纳每一个可能的值。因此,每个项目都会有一个独特的插槽。虽然这种方法对于少量项目是可行的,但当可能性数量很大时就不实用了。
因此,我们可以构造我们的哈希函数来执行相同的操作,但在构造我们自己的哈希函数时必须注意的事情。
一个好的哈希函数应该具有以下属性:
如果我们考虑上面的例子,我们使用的哈希函数是字母的总和,但是如果我们仔细检查哈希函数,那么问题可以很容易地形象化,对于不同的字符串,哈希函数开始生成相同的哈希值。
例如:{“ab”,“ba”} 具有相同的哈希值,字符串{“cd”,“be”}也生成相同的哈希值等。这称为冲突,它会在搜索中产生问题、值的插入、删除和更新。
散列过程为大密钥生成较小的数字,因此两个密钥有可能产生相同的值。新插入的键映射到已占用的键的情况,必须使用某种碰撞处理技术来处理。
处理碰撞主要有两种方法:
其思想是使散列表的每个单元指向具有相同散列函数值的记录的链表。链接很简单,但需要表外的额外内存。
示例:我们给定了一个哈希函数,我们必须使用单独的链接方法在哈希表中插入一些元素以实现冲突解决技术。
h(5) = key % 5,
元素 = 12、15、22、25 和 37。
让我们逐步看看如何解决上述问题:
12%5=2
计算得到。22%5=2
。但存储桶 2 已经被键 12 占用。15%5=0
。因此,以这种方式,使用链接方法作为冲突解决技术。
在开放寻址中,所有元素都存储在哈希表本身中。每个表条目包含一条记录或 NIL。查找元素时,我们会逐个检查表槽,直到找到所需元素或者明确该元素不在表中。
在线性探测中,从哈希的原始位置开始顺序搜索哈希表。如果我们得到的位置已被占用,那么我们检查下一个位置。
算法:
0.计算哈希键。即 key=数据%大小
1.检查 hashTable[key] 是否为空
直接通过hashTable[key] = data 存储值
2.如果哈希索引已经有一些值那么
使用key = (key+1) % size 检查下一个索引
3.检查下一个索引是否可用 hashTable[key] 然后存储该值。否则尝试下一个索引。
4.重复上述过程,直到找到空间。
示例: 让我们考虑一个简单的哈希函数“key mod 5”,要插入的键序列是 50、70、76、85、93。
50%5=0
。因此将其插入插槽号 0 中。将 50 插入哈希表
将 70 插入哈希表
76%5=1
,但 70 已经位于插槽编号 1,因此,搜索下一个空插槽并将其插入。将 76 插入哈希表
将 93 插入哈希表
二次探测是计算机编程中的一种开放寻址方案,用于解决哈希表中的哈希冲突。二次探测的操作方式是获取原始哈希索引并添加任意二次多项式的连续值,直到找到空槽。
使用二次探测的示例序列是:
H + 1 2、H + 2 2、H + 3 2、H + 4 2 ……………… H + k 2
该方法也称为中方法,因为在该方法中,我们在第 i 次迭代中查找第 i 个探针(槽),并且 i = 0, 1, ... 的值。。。n – 1。我们总是从原始哈希位置开始。如果只有该位置被占用,那么我们检查其他插槽。
令 hash(x) 为使用哈希函数计算的槽索引,n 为哈希表的大小。
如果槽 hash(x) % n 已满,那么我们尝试 (hash(x) + 1 2 ) % n。 如果 (hash(x) + 1 2 ) % n 也满了,那么我们尝试 (hash(x) + 2 2 ) % n。 如果 (hash(x) + 2 2 ) % n 也满了,那么我们尝试 (hash(x) + 3 2 ) % n。将对 i 的所有值重复此过程,直到找到空槽为止
示例:让我们考虑表大小 = 7,哈希函数为 Hash(x) = x % 7 ,冲突解决策略为 f(i) = i 2。插入 = 22、30 和 50
哈希表
将键 22 和 30 插入哈希表中
在哈希表中插入键 50
双散列是开放寻址散列表中的一种冲突解决技术。双散列利用两个散列函数,
这种哈希函数的组合具有以下形式
h(k, i) = (h1(k) + i * h2(k)) % n
双哈希算法的复杂度:
时间复杂度:O(n)
示例: 将键 27, 43, 692, 72 插入大小为 7 的哈希表中。其中第一个哈希函数是h1(k) = k mod 7,第二个哈希函数是h2(k) = 1 + (k mod 5)
在哈希表中插入键 27
h new = [h1(692) + i * (h2(692)] % 7
= [6 + 1 * (1 + 692 % 5)] % 7
= 9 % 7
= 2
现在,由于 2 是一个空槽,
所以我们可以将 692 插入第二个插槽。
在哈希表中插入键 692
h new = [h1(72) + i * (h2(72)] % 7
= [2 + 1 * (1 + 72 % 5)] % 7
= 5 % 7
= 5
现在,因为 5 是一个空槽,
所以我们可以将 72 插入到第 5 个槽中。
在哈希表中插入键 72
哈希表的负载因子可以定义为哈希表包含的项数除以哈希表的大小。负载因子是当我们想要重新哈希以前的哈希函数或想要向现有哈希表添加更多元素时使用的决定性参数。
它帮助我们确定哈希函数的效率,即它告诉我们正在使用的哈希函数是否在哈希表中均匀分布键。
负载因子=哈希表中的元素总数/哈希表的大小
顾名思义,重新哈希意味着再次哈希。基本上,当负载因子增加到超过其预定义值(负载因子的默认值为 0.75)时,复杂性就会增加。因此,为了克服这个问题,数组的大小增加(加倍),所有值再次散列并存储在新的双倍大小的数组中,以保持低负载因子和低复杂性。
从上面的讨论中,我们得出结论,散列的目标是解决在集合中快速查找项目的挑战。例如,如果我们有数百万个英语单词的列表,并且我们希望找到一个特定术语,那么我们将使用散列来更有效地定位和查找它。在找到匹配项之前检查数百万个列表中的每个项目是低效的。散列通过在开始时将搜索限制为较小的单词集来减少搜索时间。
阅读量:1566
点赞量:0
收藏量:0