【算法基础】哈希算法

目录

7.1哈希表(散列表)

7.1.1直接寻址法

7.1.2除法哈希法

7.1.3哈希碰撞

7.1.4评价

7.1.5代码实现

7.2一致性hash

7.3局部敏感哈希(LSH)

7.3.1为什么要有局部敏感哈希?

7.3.2文档相似度计算

7.3.3文档shingling

7.3.4保持相似度矩阵表示

7.4.5最小哈希

参考文档:


7.1哈希表(散列表)

7.1.1直接寻址法

举个例子:将nums=[1,3,5,4,6,1001],让数字和数组的下标对应,更加数组的下标就可以找对应的元素nums[1001]
优点:速度快,一次性找到结果
缺点:数据差异大的时候极大的浪费了空间

7.1.2除法哈希法

分析:让我们的目标树和长度计算余数,作为下标用来存储数据,可以让一个很小的数组存储非常大的数据.

举例:nums=[1,9,7,8,10],长度为5,存放的数据为:[10,1,7,8,9]

缺点:这时候如果我们将数字9改成数字6应该怎么存放尼?

这时候就出现了hash冲突(哈希碰撞),有两种解决方法.

7.1.3哈希碰撞

1.解决方法一:开发寻址法(不好)

如果出现了哈希碰撞,我们就将得到的数值存储到与它相邻的节点,如果相邻的节点也是满的,我们可以继续将它存储到下一个节点,一次类推,总能存储到,在进行查找的时候,我们可以对该数取余,再进行查找.

2.解决方法二:拉链法

在每个节点中我们不存放数据,我们存放链表,链表是变动的,链表的大小会随着存入数据的多少进行变动,我们可以将产生哈希碰撞的数据存放在链表中.

7.1.4评价

一个好的哈希算法可以使得程序的查找速度变得非常快,进行哈希映射之后,一个好的哈希算法能够让数据分布均匀,数据分布的越均匀,查询的速度越快

7.1.5代码实现

class HashTable:
    def __init__(self,size):
        self.size=size # 哈希表的长度
        self.lis=[[] for i in range(5)]  # 存放数据的列表,其实这里应该是链表,这里我们就用列表代替

    # 对数据进行哈希运算
    def hash(self,data):
        return data%self.size

    # 数据的查找
    def find(self,data):
        h=self.hash(data)  # 对数据进行hash运算,得到数据应该存放的下标
        lis=self.lis[h]  # 获取数据存放对应的列表
        if data in lis:  # 如果这个数据在这个列表中,说明我们找到了,否则没有找到
            return True
        else:
            return False

    # 数据的存储
    def put(self,data):
        h=self.hash(data)  # 对数据进行hash运算,得到数据应该存放的下标
        lis=self.lis[h]  # 获取数据存放对应的列表
        if data not in lis:  # 如果数据在对应的列表中不存在,我们可以将数据添加到该对应的列表中
            lis.append(data)

if __name__ == '__main__':
    h=HashTable(6)
    h.put(1)
    h.put(3)
    h.put(7)
    h.put(4)
    h.put(8)
    h.put(13)
    print(h.lis)

 

7.2一致性hash

分布式缓存:

我们要将一个图片存放在S1,S2,S3台服务器上,为了分配均匀,我们使用:hash(图片名)%机器数=余数,对应的余数就是存放数据的编号

但是如果我们想要增加一台服务器该怎么办?如果我们增加一台服务器,那么对应的位置上的图片将会访问不到,就会造成缓存失效,进而导致服务器压力过大导致缓存穿透(缓存服务器内容不要充分利用,即使出现其中某台服务器瘫痪,其他服务器也能正常工作),因此我们应该使用一致性hash算法.

我们有2的32次方张图片,我们对服务器进行hash运算之后对其进行2的32次方取余得到结果,我们可以在hash环上多增加几台虚拟服务器例如服务器A我们可以映射几个虚拟服务器A0,A1,A2等,这样即使增加了服务器也不会对结果影响太多.具体请参看大佬视频

hash偏斜:

  • 缓存分布不均匀---系统故障
  • 最好让服务器尽量多--增加虚拟节点
  • A----a1,a2,a3...
  • 虚拟节点越多越均匀
  • 缓存读写--虚拟节点--真实节点--读写

7.3局部敏感哈希(LSH)

7.3.1为什么要有局部敏感哈希?

如果只是要找到重复的微博,我们可以用两两比较所有的微博,对相同的微博值保留一条即可;但这只能在数据量很小的情况下才有可能,当我们有1000万条微博时,需要两两比较的微博有10^6亿(n*(n-1)/2)对,这个计算量是惊人的,即便你用map-reduce,拥有强大的集群,那也顶不住数据再增加一两个数量级。一种稍微好一点的办法是对所以微博进行一次hash,再对桶内的微博进行比较,利用hash可以过滤了绝大多数不相同的微博,避免了无谓的比较,时间复杂度O(n),显著提高效率。Hash的一个基本特性就是随机性,它将一个字符串随机的hash到一个桶中,对于相同的两个字符串,hash值总是相同的,但两个hash值只要相差一点,hash值就就可能大相径庭,可谓差之毫厘谬以千里

s1='hello gfb'
s2='hello gfb'
s3='hellogfb'
print(hash(s1),hash(s2),hash(s3))

如果能有一种hash算法,能将相似的字符串hash得到相似的hash值,那就能在接近线性的时间解决海量微博中的去重问题。幸好,这样的hash已经有大牛发明了,它叫局部敏感哈希

7.3.2文档相似度计算

度量距离的方式很多:欧式距离、编辑距离、余弦距离、Jaccard距离.距离和相似度是不同的概念,距离越近相似度应该越高,距离越远相似度应该越低,因此similar = 1-distace。

集合S和T的Jaccard相识度sim(s,t)=(s交t)/(s并t)当我们不考虑微博中重复出现的词时,一条微博就可以看成一个集合,集合的元素是一个个的词

s1 = '''从 决心 减肥 的 这 一刻 起 请 做 如下 小 改变 你 做 得 到 么'''
s2 = '''从 决心 减肥 的 这 一刻 起 请 做 如下 小 改变'''

sim(s1,s2)=11/16=0.69.

7.3.3文档shingling

{"从 决心","决心 减肥","减肥 的","的 这","这 一刻","一刻 起","起 请","请 做","做 如下","如下 小","小 改变"}

为了字面上相似的文档,将文档表示成集合最有效的方法是构建文档中的短字符串集合;k值的选取具有一定的技巧,k越大越能找到真正相似的文档,而k越小就能召回更多的文档,但他们可能相似度不高,比如k=1,就变成了基本词的比较了。我这里词作为shingle的基本单位,在英文处理中,是以字母为基本单位,原因在于汉字有上万个,而英文字母只有27个,以汉字为单位将造成shingle集合巨大。遍历所用文档,就得到了shingle全集。

7.3.4保持相似度矩阵表示

s1 = "我 减肥"
s2= "要"
s3 = "他 减肥 成功"
s4 = "我 要 减肥"

实际上,真正计算的过程中矩阵不是这样表示的,因为数据很稀疏。得到矩阵表示后,我们来看最小hash的定义。

7.4.5最小哈希

最小hash定义为:特征矩阵按行进行一个随机的排列后,第一个列值为1的行的行号。举例说明如下,假设之前的特征矩阵按行进行的一个随机排列如下:

具体内容可查看大佬文章

参考文档:

哈希表

局部敏感哈希

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值