HASH算法

1基本原理


我先提一个简单的问题,在一个一百万首歌中,如何快速找出你要的那一首歌?当然歌名必须相同,也许你会想,只有一百万,全部查找一遍不就好了?问题是,一首歌的名字也有长度,每次必须从头到尾扫描一遍,最后就会很慢了。Hash 算法就可以解决这个问题,也就是一个优化时间复杂度的算法。


Hash 算法的基本原理就是,把一个字符串(或是一串数)压缩为一个数,具体压缩的方法为一个给定的哈希函数。比如定义 a 对应 1,b 对应 2,c 对应 3;每个位置上生成一个随机数,字符串的哈希值即为每个字符对应的数 × 随机数之和。如果两个字符串完全相同,哈希值也应该相同;但如果两个字符串不同,哈希值相同的概率就极低了。


另外,通过哈希函数可以把字符串轻松转成一个数值,但如果知道数值转化为字符串,就非常困难了。因此哈希函数是单向的。


这相当于一个 “ 赌 ” 的游戏,即赌字符串不同,哈希值就不会相同。为了使概率极低,需要把随机数生成得越大越好,即哈希函数十分重要。如果把哈希函数设计得过于简单,那非常容易就会出现矛盾。


2滚动哈希(Rolling Hash)


我再提出一个问题,如何快速判断一个长度为 m 的字符串,是另一个长度为 n 的字符串的子串?其中 n 比较大,m 比较小,如果枚举所有较长字符串中的所有长度为 m 的子串,再一一判断,是会超时的。用哈希的想法是,将长度为 m 的字符串,转化为一个哈希值,然后与子串比较哈希值。


不过这样做有一个问题,每一个子串的哈希值不还是要 O(m) 扫描一遍求吗?其实你会发现,如果枚举起点,固定终点,每次仅往后挪了一格。此时如果重新计算哈希值,会有大量重复,因此不划算。


按照我们之前举过的例子定义哈希函数,每次挪动一格,只需删除起点处格子的哈希值,增加终点处格子的哈希值,即可达到目的。现在问题又来了,挪动一格位置加一的同时,随机数没有改变呀?其实解决方案并不困难,只要 “ 随机数 ” 为 m 的幂次即可,即每往后挪动一个,中间的格子集体乘以 m。这样相当于一个 m 进制数,更可以保证哈希值的唯一性,如下图所示。


其实比较子串的算法有不止一种,比如 KMP 算法。上述的做法就是大名鼎鼎的 Rabin - Karp 算法,其中的 Rabin 是不是很眼熟呢?就是 Miller-Rabin 算法中的 Rabin 哦,点击下面的链接回顾这个判断质数的算法:


一分钟算法——Miller-Rabin 质数判断法


用类似滚动哈希的方法,可以避免重复,大大增加效率,用哈希的思想来解决判断子串等问题。


3后记


最后来谈谈哈希算法的具体应用,主要用于 “ 压缩 ”。在上世纪打仗发电报时,通常用哈希来进行 “ 确认 ”(类似加密,哈希即那个时候发明)。比如德国的潜水艇与德国指挥部用相同的哈希函数,为了防止电报被敌方收到后,修改再发送,因此只要在传送时附上这个字符串的哈希值,然后与收到的比对一下即可。如果不同,说明这条消息是不正确的,不能听从指令。当然,为了防止敌方破译密码,可能会每天修改一次哈希函数。


说到加密,就有必要提一提令我影响深刻的电影《U-571》了,这部精彩紧张的电影让生活在现代的我,也能感受到当时打仗的激烈和密码机的重要。欢迎阅读我的 “ 科普 ” 电影介绍:


电影《U-571》中的物理


读到这里,是不是觉得哈希算法相当有用呢?不止歌曲搜索,在各种搜索网站,比如百度,用到的底层思想都是哈希算法。不然在成千上万的网页中,怎么会快速找出关键词呢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值