算法与数据结构——哈希算法

news/2024/11/14 22:37:11/文章来源:https://www.cnblogs.com/1873cy/p/18384976

哈希算法

前面介绍了哈希表的工作原理和哈希冲突的处理方法。然而无论是开放寻址还是链式地址,它们只能保证可以在发生冲突时正常工作,而无法减少哈希冲突的发生

如果哈希冲突过于频繁,哈希表的性能则会急剧劣化。如下图所示,对于链式哈希表,理想情况下键值对均匀分布在各个桶中,达到最佳查询效率;最差情况所有键值对都存储到同一个桶中,时间复杂度退化至 O(n)

键值对的分布情况由哈希函数决定。在前面的哈希表实现中,哈希函数是直接对键取数组长度的模:

        /*哈希函数*/int hashFunc(int key){int index = key % capacity;return index;}

index = hash(key) % capacity
观察以上公式,当哈希表容量capacity固定时,哈希算法hash()决定了输出值,进而决定了键值对在哈希表中的分布情况。

为降低哈希冲突发生的概率,我们应当将注意力集中在哈希算法hash()的设计上 。

哈希算法的目标

为了实现“既快又稳”的哈希表数据结构,哈希算法应该具备以下特点:

  • 确定性: 对于相同的输入,哈希算法应始终产生相同的输出。这样才能确保哈希表时可靠的。
  • 效率高: 计算哈希值的过程应该足够快。计算开销越小,哈希表的实用性越高。
  • 均匀分布: 哈希算法应使得键值对均匀分布在哈希表中。分布越均匀,哈希冲突的概率就越低。

实际上哈希算法除了可以用于实现哈希表,还广泛应用于其他领域中。

  • 密码存储:为了保护用户密码的安全,系统通常不会直接存储用户的明文密码,而是存储密码的哈希值。当用户输入密码时,系统会对输入的密码计算哈希值,然后与存储的哈希值进行比较。如果两者匹配,那么密码就被视为正确。
  • 数据完整性检查:数据发送方可以计算数据的哈希值并将其一同发送;接收方可以重新计算接收到的数据的哈希值,并与接收到的哈希值进行比较。如果两者匹配,那么数据就被视为完整。

对于密码学的相关应用,为了防止从哈希值推导出原始密码登逆向工程,哈希算法需要具备更高等级的安全特性。

  • 单向性:无法通过哈希值反推出关于输入数据的任何信息。
  • 抗碰撞性:应当极难找到两个不同的输入,使得它们的哈希值相同。
  • 雪崩效应:输入的微小变化应当导致输出的显著且不可预测的变化。

注意:“均匀分布”与“抗碰撞性”是两个独立的概念,满足均匀分布不一定满足抗碰撞性。例如,在随机输入key下,哈希函数key % 100可以产生均匀分布的输出,但该哈希算法过于简单,所有后两位相等的key的输出都相同,因此我们可以很容易地从哈希值反推出可用的key,从而破解密码。

哈希算法的设计

哈希算法的设计是需要考虑多项因素的复杂问题。然而对于某些要求不高的场景,我们也能设计一些简单的哈希算法。

  • 加法哈希:对输入的每个字符的ASCII码进行相加,将得到的总和作为哈希值。
  • 乘法哈希:利用乘法的不相关性,每轮乘一个常数,将各字符的ASCII码积累到哈希值中。
  • 异或哈希:将输入的每个元素通过异或操作累积到一个哈希值中。
  • 旋转哈希:将每个字符的ASCII码累积到一个哈希值中,每次累积之前都会对哈希值进行旋转操作。
/*加法哈希*/
int addHash(string key){long long hash = 0;/*1000000007 是一个质数,使用质数作为模数可以减少哈希冲突的概率*//*避免内存溢出,保证结果在int整数范围内*/const int MODULE = 1000000007;/*使用unsigned char保证字符转换后都为正数,避免了减法的发生*/for (unsigned char ch : key){hash = (hash + (int)ch) % MODULE;}return (int)hash;
}
/*乘法哈希*/
int mulHash(string key){long long hash = 0;const int MODULE = 1000000007;for (unsigned char ch : key){hash = (31 * hash + (int)ch) % MODULE;}return (int)hash;
}
/*异或哈希*/
int xorHash(string key){long long hash = 0;const int MODULE = 1000000007;for (unsigned char ch : key){hash ^= (int)ch;}return (int)hash;
}
/*旋转哈希*/
int rotHash(string key){long long hash = 0;const int MODULE = 1000000007;for (unsigned char ch : key){hash = ((hash << 4)^(hash >> 28) ^ (int)ch) % MODULE;}return (int)hash;
}

观察发现每一种哈希算法的最后一步都是对大质数1000000007取模,以确保哈希值在合适的范围内。

原因:使用大质数作为模数,可以最大化地保证哈希值均匀分布。因为质数不与其他数字存在公约数,可以减少因取模操作而产生的周期性模式,从而避免哈希冲突。
举个例子,假设我们选择合数9作为模数,它可以被3整除,那么所有可以被3整除的key都会被映射到0、3、6这三个哈希值。

如果输入key恰好满足这种等差数列的数据分布,那么哈希值就会出现聚堆,从而加重哈希冲突。现在,假设将module替换为质数13,由于keymodule之间不存在公约数,因此输出的哈希值的均匀性会明显提升。

说明:如果能保证key是随机均匀分布的,那么选择质数或者合数作为模数都是可以的,它们都能输出均匀分布的哈希值。而当key的分布存在某种周期性时,对合数取模更容易出现聚集现象。

总而言之,我们通常选取质数作为模数,并且这个质数最好足够大,以尽可能消除周期模式,提升哈希算法的稳健性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/788640.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Proxyless的多活流量和微服务治理

1. 引言 1.1 项目的背景及意义 在当今的微服务架构中,应用程序通常被拆分成多个独立的服务,这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性,但也带来了新的挑战,比如:服务间通信的复杂性:不同服务之间需要进行可靠的通信,处理失败重试、负…

报表融合大屏,做不一样的财务分析!

冷冰冰的数据如何让人眼前一亮? 千篇一律的表格如何让数据可视化? ...... 赶快丢掉那些传统的表格工具吧!!!现在我们都用更智能的工具来做报表了!财务报表是什么? 财务报表是企业财务状况、经营成果及现金流量的综合反映,通过表格、图表等形式,系统地展示了企业在一定…

Cloudflare Workers 每日免费限制 超出流量自动关闭 - 失败模式 改为 失败时自动关闭(阻止)

cloudflare workers 每日免费限制 超出流量自动关闭 - 失败模式 改为 失败时自动关闭(阻止) 位置在 Workers 和 Pages - 相应的workers - 设置 - 函数 - 更改失败模式 改为 失败时自动关闭(阻止) 这个设置,网上竟然没有人说,这么重要的事情,应该要设置,必须要设置!!…

为什么上海市的跨江大桥两边上没有设置非机动车道 All In One

为什么上海市的跨江大桥两边上没有设置非机动车道 All In One 处于设计、安全和成本考虑,因噎废食,懒得搞为什么上海市的跨江大桥两边上没有设置非机动车道 All In One处于设计、安全和成本考虑,因噎废食,懒得搞FQA 行人如何跨越黄浦江?上海15座跨江大桥,为何只见车流不见…

vue3的 状态管理库

1.vue 的状态管理库 vue 常用的状态管理库有 vuex 和 Pinia,两者的区别如下:架构设计‌:Vuex 采用全局单例模式,通过一个store对象来管理所有的状态;而 Pinia 采用了分离模式,每个组件都拥有自己的store实例。 模块设计:Vuex 包含 states、mutations、getters、actions、…

南沙信息学家教陈老师: 1349:【例4-10】最优布线问题

​ 【题目描述】学校有nn台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们有数据线连接。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。 当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。为了节…

基于springboot的grpc服务端demo

一个springboot 实现grpc 服务端demo的简单配置,包含java配置 和 代理配置1. Javamaven配置点击查看代码 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www…

博客园美化系列第一弹

博客园美化系列第一弹 首先要确保你已经申请开通博客「理由随便写,积极向上即可」,且已通过审核。 然后进入【设置】,申请 js 权限。 申请理由举例「从网上找的,当时直接复制上就通过了」: 尊敬的博客园管理员: 您好,我想通过 js 定制化我的博客,麻烦通过下我的申请。 …

高通ramdump

背景 高通平台下提供了一个工具,专门用来抓取内核死机以后的dump信息。如果只是非系统层面的crash(例如底层应用,安卓程序),则不能抓取dump信息。 在阅读一些文档的时候知道有这个功能,但是一直没时间尝试。 介绍 流程为: 1、进入dump模式:系统需要触发crash, 同时机器…

QL5010-16-ASEMI逆变焊机专用整流桥QL5010

QL5010-16-ASEMI逆变焊机专用整流桥QL5010编辑:ll QL5010-16-ASEMI逆变焊机专用整流桥QL5010 型号:QL5010 品牌:ASEMI 封装:KBPC-4 批号:2024+ 类型:整流模块 电流:50A 电压:1600V 安装方式:直插式封装 特性:大功率、整流桥 产品引线数量:4 产品内部芯片个数:4 产品…

解决方案 | IrfanView如何滑动滚轮图像缩放?

这是个bug,已经很多人反映了。目前没有比较好的解决方法,还是使用ctrl+滚轮最好。如果需要设置滚轮放大的话,按照下图即可,但是带来一个bug,你无法通过方向键或者菜单的箭头浏览 下一张图片。综上所述,你有3个选择,1 接受使用 ctrl+滚轮进行放大2 设置--关闭”显示所有支…

Python开发工具:VSCode+插件

本篇是 Python 系列教程第 3 篇,更多内容敬请访问我的 Python 合集Visual Studio Code的安装非常简单,就不放这里增加文章篇幅了。 相比PyCharm,VSCode更加轻量,启动速度快。并且搭配Python插件就能实现和Pycharm一样的代码提示、高亮效果。 1 安装插件 安装插件也非常简单…