python 哈希(hash)

本文介绍了Python中的哈希(Hash)及其应用,重点讨论了散列表(Hash table)的工作原理和优势。哈希表是一种利用散列函数实现快速查找、插入和删除的数据结构,其负载因子决定了性能。文章提到了三种常见的散列方法:除法散列法、平方散列法和斐波那契(Fibonacci)散列法,并探讨了哈希表在海量数据处理中的重要性。

python 哈希(hash)
散列表(Hash table)–哈希表
基于高度存取 ,一种典型的“空间换时间”
可以理解为一个线性表,其中元素不是紧密排列,可能存在空隙
散列表,依据关键码值(key value)而进行访问的数据结构,即,它通过把关键码值
映射到表中一个位置来访问记录,以加快查找速度,这个映射函数叫做散列函数,
存放记录的数组叫做散列表

eg
记录的存储位置 = f(关键字)
这里的对应关系f称为散列函数,又称为哈希,采用散列技术将记录存储在一块连续的存储空间中
这块连续存储空间称为散列或者哈希表(Hash table)

使用哈希表进行查询–使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value
(充分利用数组的定位性能进行数据定位)

数组特点:寻址容易,插入和删除困难
链表特点:寻址困难,插入和删除容易
哈希表:寻址容易,插入删除也容易的数据结构

比方我们存储70个元素,但我们可能为这70个元素申请了100个元素的空间。
70/100=0.7,这个数字称为负载因子----高速存取
基于一种结果尽可能随机平均分布的固定函数H为每一个元素安排存储位置,这样就能够避免遍历性质的线性搜索
拉链法:链表的数组
根据元素的一些特征把元素分配到不同的链表中去,
也是根据这些特征,找到正确的链表,再从链表中找出这个元素。
Hash的应用
信息安全领域中加密算法;Hash就是找到一种数据内容和数据存放地址之间的映射关系
查找;哈希表是完全另外一种思路:当我知道key值以后,我就可以直接计算出这个元素在集合中的位置,根本不需要一次又一次的查找
Hash表在海量数据处理中有着广泛应用
Hash---------O(1)的时间复杂度
散列表的查找步骤
当存储记录时,通过散列函数计算出记录的散列地址
当查找记录时,我们通过同样的是散列函数计算记录的散列地址,并按此散列地址访问该记录

关键字——散列函数(哈希函数)
——散列地址

优点:一对一的查找效率很高;

	缺点:一个关键字可能对应多个散列地址;
		需要查找一个范围时,效果不好。


散列冲突:不同的关键字经过散列函数的计算得到了相同的散列地址。


好的散列函数=计算简单+分布均匀(计算得到的散列地址分布均匀)


哈希表是种数据结构,它可以提供快速的插入操作和查找操作

优缺点

优点:不论哈希表中有多少数据,查找、插入、删除(有时包括删除)
只需要接近常量的时间即0(1)的时间级。实际上,这只需要几条机器指令。

哈希表运算得非常快,在计算机程序中,
如果需要在一秒种内查找上千条记录通常使用哈希表(例如拼写检查器)
哈希表的速度明显比树快,树的操作通常需要O(N)的时间级。
哈希表不仅速度快,编程实现也相对容易。

如果不需要有序遍历数据,并且可以提前预测数据量的大小。
那么哈希表在速度和易用性方面是无与伦比的。

缺点:它是基于数组的,数组创建后难于扩展,某些哈希表被基本填满时,
性能下降得非常严重,所以程序员必须要清楚表中将要存储多少数据
(或者准备好定期地把数据转移到更大的哈希表中,这是个费时的过程)。

元素特征转变为数组下标的方法就是散列法。散列法当然不止一种,下面列出三种比较常用的:

1,除法散列法 
最直观的一种,上图使用的就是这种散列法,
公式: 
      index = value % 16

学过汇编的都知道,求模数其实是通过一个除法运算得到的,所以叫“除法散列法”。

2,平方散列法 
求index是非常频繁的操作,而乘法的运算要比除法来得省时
(对现在的CPU来说,估计我们感觉不出来),
所以我们考虑把除法换成乘法和一个位移操作。
公式: 
      index = (value * value) >> 28   
(右移,除以2^28。记法:左移变大,是乘。右移变小,是除。)

如果数值分配比较均匀的话这种方法能得到不错的结果,
也许你还有个问题,value如果很大,value * value不会溢出吗?
答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,
而是为了获取index。

3,斐波那契(Fibonacci)散列法

平方散列法的缺点是显而易见的,
所以我们能不能找出一个理想的乘数,而不是拿value本身当作乘数呢?答案是肯定的。

1,对于16位整数而言,这个乘数是40503 

2,对于32位整数而言,这个乘数是2654435769 

3,对于64位整数而言,这个乘数是11400714819323198485

这几个“理想乘数”是如何得出来的呢?这跟一个法则有关,叫黄金分割法则,
而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,
即如此形式的序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377, 610, 987, 1597, 2584, 4181, 6765, 10946,…。
另外,斐波那契数列的值和太阳系八大行星的轨道半径的比例出奇吻合。

对我们常见的32位整数而言,
公式: 
            index = (value * 2654435769) >> 28

注:用斐波那契散列法调整之后会比原来的取摸散列法好很多。

适用范围

快速查找,删除的基本数据结构,通常需要总数据量可以放入内存。

基本原理及要点
    hash函数选择,针对字符串,整数,排列,具体相应的hash方法。

碰撞处理,一种是open hashing,也称为拉链法;
另一种就是closed hashing,也称开地址法,opened addressing。

散列冲突的解决方案:

1.建立一个缓冲区,把凡是拼音重复的人放到缓冲区中。当我通过名字查找人时,
发现找的不对,就在缓冲区里找。

2.进行再探测。就是在其他地方查找。探测的方法也可以有很多种。

(1)在找到查找位置的index的index-1,index+1位置查找,index-2,index+2查找,依次类推。这种方法称为线性再探测。


(2)在查找位置index周围随机的查找。称为随机在探测。


(3)再哈希。就是当冲突时,采用另外一种映射方式来查找。


这个程序中是通过取模来模拟查找到重复元素的过程。对待重复元素的方法就是再哈希:对当前key的位置+7。
最后,可以通过全局变量来判断需要查找多少次。

显然,当总的查找次数/查找的总元素数越接近1时,哈希表更接近于一一映射的函数,查找的效率更高

扩展 
   
d-left hashing中的d是多个的意思,我们先简化这个问题,看一看2-left hashing。
2-left hashing指的是将一个哈希表分成长度相等的两半,分别叫做T1和T2,
给T1和T2分别配备一个哈希函数,h1和h2。在存储一个新的key时,
同 时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]。
这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,
哪一个 位置已经存储的(有碰撞的)key比较多,
然后将新key存储在负载少的位置。如果两边一样多,
比如两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1子表中,
2-left也由此而来。在查找一个key时,必须进行两次hash,同时查找两个位置。

问题实例(海量数据处理)
我们知道hash 表在海量数据处理中有着广泛的应用,下面,请看另一道百度面试题:
题目:海量日志数据,提取出某日访问百度次数最多的那个IP。
方案:IP的数目还是有限的,最多2^32个,所以可以考虑使用hash将ip直接存入内存,然后进行统计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值