版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。

前面给大家讲了哈希表(散列)这种数据结构,那么使用哈希表来解决实际问题,那就是Hash算法了,我们一起来看看。

一、Hash算法的概念

Hash算法(Hash Algorithm),简称散列算法,也成哈希算法(英译),是将一个大文件映射成一个小串字符。与指纹一样,就是以较短的信息来保证文件的唯一性的标志,这种标志与文件的每一个字节都相关,而且难以找到逆向规律。

举个列子:

服务器存了10个文本文件,你现在想判断一个新的文本文件和那10个文件有没有一个是一样的。你不可能去比对每个文本里面的每个字节,很有可能,两个文本文件都是5000个字节,但是只有最后一位有所不同,但这样的,你前面4999位的比较就是毫无意义。那一个解决办法,就是在存储那10个文本文件的时候,都将每个文件映射成一个hash字符串。服务器只需要存储10个hash字符串,在判断的时候,只需要判断新的这个文本文件的hash值是否和那10个文件的hash值一致,那就可以解决这个问题了。

由于文件是无限的,而映射后的字符串能表示的位数是有限的。因此可能会存在不同的key对应相同的Hash值。这就存在碰撞的可能。

二、Hash算法的应用

java的数据结构中,很多类都有用到hash算法,比如String,HashMap。

1.String中的hashCode方法

  1.  
  1. public int hashCode() {
  2. int h = hash;
  3. if (h == 0 && value.length > 0) {
  4. char val[] = value;
  5.  
  6. for (int i = 0; i < value.length; i++) {
  7. h = 31 * h + val[i];
  8. }
  9. hash = h;
  10. }
  11. return h;
  12. }
  1.  

可以看到,这里使用了31来作为乘级因子,这是为什么呢?

  1. 选择数字31是因为它是一个奇质数,如果选择一个偶数,因为乘二相当于左移一位,可能会产生溢出,导致数值信息溢出。
  2. 这一点的优势并不明显,但这是一个传统(选择质数)。
  3. 同时,数字31有一个很好的特性,乘法运算可以被移位和减法运算来取代,来获取更好的性能,而且这一点可以由jvm来自动完成。31*i=(i<<5)-i

2.HashMap中hash值

存在的目的是加速键值对的查找,key的作用是为了将元素适当的放在各个桶里,对于抗碰撞的要求没那么高。

  1.  
  1. static final int hash(Object key) {
  2. int h;
  3. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  4. }
 

对key的hash计算,就是计算出key的hash值,并移动到低位,完成高低位的融合。

3.Hash在密码学中的应用

hash算法在密码学中主要是用于消息摘要和签名。换句话说,主要是对整个消息的完整性进行校验。

安全散列算法(英语:Secure Hash Algorithm,缩写为SHA),SHA家族的五个算法,分别是SHA-1、SHA-224、SHA-256、SHA-384,和SHA-512,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布;是美国的政府标准。后四者有时并称为SHA-2。

三、使用hash来解决字符串判重/字符串匹配问题

  1. 遇见不定长问题可通过二分+hash降低复杂度
  2. 遇见定长字符串问题可通过尺取+hash来降低复杂度
  3. 二维hash的时候尺取方法就是把之前不需要的都变为0再加上当前行,将匹配字符串整体下移,来验证hash值是否相等

PS:尺取法:顾名思义,像尺子一样取一段,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。

例子1:

给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

腾讯面试题:A.txt和B.txt两个文件,A有1亿个qq号,B有100万个,用代码实现交、并、差...

1.思路

可以估计每个文件安的大小为5G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。

  1. 遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999)中。这样每个小文件的大约为300M。
  2. 遍历文件b,采取和a相同的方式将url分别存储到1000小文件(记为b0,b1,...,b999)。这样处理后,所有可能相同的url都在对应的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
  3. 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了

2.代码实现

参考自两个上亿行的大文件取交集

TODO:Java实现版本还未写

PS:如果允许小误差,也可以采用如下的布隆算法实现

  1.  
  1. import java.util.ArrayList;
  2. import java.util.BitSet;
  3. import java.util.List;
  4.  
  5. /**
  6. * BloomFilter算法
  7. *
  8. * @author JYC506
  9. *
  10. */
  11. public class BloomFilter {
  12. /*哈希函数*/
  13. private List<IHashFunction> hashFuctionList;
  14. /*构造方法*/
  15. public BloomFilter() {
  16. this.hashFuctionList = new ArrayList<IHashFunction>();
  17. }
  18. /*添加哈希函数类*/
  19. public void addHashFunction(IHashFunction hashFunction) {
  20. this.hashFuctionList.add(hashFunction);
  21. }
  22. /*删除hash函数*/
  23. public void removeHashFunction(IHashFunction hashFunction) {
  24. this.hashFuctionList.remove(hashFunction);
  25. }
  26. /*判断是否被包含*/
  27. public boolean contain(BitSet bitSet, String str) {
  28. for (IHashFunction hash : hashFuctionList) {
  29. int hashCode = hash.toHashCode(str);
  30. if(hashCode<0){
  31. hashCode=-hashCode;
  32. }
  33. if (bitSet.get(hashCode) == false) {
  34. return false;
  35. }
  36. }
  37. return true;
  38. }
  39. /*添加到bitSet*/
  40. public void toBitSet(BitSet bitSet, String str) {
  41. for (IHashFunction hash : hashFuctionList) {
  42. int hashCode = hash.toHashCode(str);
  43. if(hashCode<0){
  44. hashCode=-hashCode;
  45. }
  46. bitSet.set(hashCode, true);
  47. }
  48. }
  49.  
  50. public static void main(String[] args) {
  51. BloomFilter bloomFilter=new BloomFilter();
  52. /*添加3个哈希函数*/
  53. bloomFilter.addHashFunction(new JavaHash());
  54. bloomFilter.addHashFunction(new RSHash());
  55. bloomFilter.addHashFunction(new SDBMHash());
  56. /*长度为2的24次方*/
  57. BitSet bitSet=new BitSet(1<<25);
  58. /*判断test1很test2重复的字符串*/
  59. String[] test1=new String[]{"哈哈","我","大家","逗比","有钱人性","小米","Iphone","helloWorld"};
  60. for (String str1 : test1) {
  61. bloomFilter.toBitSet(bitSet, str1);
  62. }
  63. String[] test2=new String[]{"哈哈","我的","大家","逗比","有钱的人性","小米","Iphone6s","helloWorld"};
  64. for (String str2 : test2) {
  65. if(bloomFilter.contain(bitSet, str2)){
  66. System.out.println("'"+str2+"'是重复的");
  67. }
  68. }
  69.  
  70. }
  71. }
  72. /*哈希函数接口*/
  73. interface IHashFunction {
  74. int toHashCode(String str);
  75. }
  76.  
  77. class JavaHash implements IHashFunction {
  78.  
  79. @Override
  80. public int toHashCode(String str) {
  81. return str.hashCode();
  82. }
  83.  
  84. }
  85.  
  86. class RSHash implements IHashFunction {
  87.  
  88. @Override
  89. public int toHashCode(String str) {
  90. int b = 378551;
  91. int a = 63689;
  92. int hash = 0;
  93. for (int i = 0; i < str.length(); i++) {
  94. hash = hash * a + str.charAt(i);
  95. a = a * b;
  96. }
  97. return hash;
  98. }
  99.  
  100. }
  101.  
  102. class SDBMHash implements IHashFunction {
  103.  
  104. @Override
  105. public int toHashCode(String str) {
  106. int hash = 0;
  107. for (int i = 0; i < str.length(); i++)
  108. hash = str.charAt(i) + (hash << 6) + (hash << 16) - hash;
  109. return hash;
  110. }
  111.  
  112. }

例子2:

现有海量日志数据保存在一个超级大的文件中,该文件无法直接读入内存,要求从中提取某天出访问百度次数最多的那个IP。

  1. 从这一天的日志数据中把访问百度的IP取出来,逐个写入到一个大文件中;
  2. 注意到IP是32位的,最多有2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件;
  3. 找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率;
  4. 在这1000个最大的IP中,找出那个频率最大的IP,即为所求。

参考分治法获取文件中出现频次最高100词

四、总结

  1. 简单点说,hash就是将任意长度的消息压缩成某一固定长度的消息摘要的函数。
  2. 可能会存在不同的key对应相同的Hash值,这就是存在碰撞的可能。
  3. Hash算法是不可逆的,即不同通过Hash值逆向推出key的值。

处理海量数据问题思路:

  1. 分而治之/hash映射 + hash统计 + 堆/快速/归并排序;
  2. 双层桶划分
  3. Bloom filter/Bitmap;
  4. Trie树/数据库/倒排索引;
  5. 外排序;
  6. 分布式处理之Hadoop/Mapreduce。

我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

参考资料:

  1. https://blog.csdn.net/u014209205/article/details/80820263
  2. https://blog.csdn.net/qq_38891827/article/details/80723483
  3. https://www.cnblogs.com/wkfvawl/p/9016281.html
  4. https://blog.csdn.net/consciousman/article/details/52348439
  5. https://bbs.csdn.net/topics/370253735
  6. https://www.cnblogs.com/aspirant/p/7154551.html
  7. https://www.bbsmax.com/A/n2d9OwP4JD
  8. https://www.2cto.com/kf/201701/586765.html
  9. https://blog.csdn.net/weixin_33737774/article/details/85891625
  10. https://blog.csdn.net/qingdujun/article/details/82343756
  11. https://blog.csdn.net/u012173884/article/details/47419163
  12. https://blog.csdn.net/samjustin1/article/details/52251180

程序员的算法课(14)-Hash算法-对海量url判重的更多相关文章

  1. murmurhash2算法 和 DJB Hash算法是目前最流行的hash算法

    murmurhash2算法 和 DJB Hash算法是目前最流行的hash算法 1.DJB HASH算法 1 2 3 4 5 6 7 8 9 10 11 /* the famous DJB Hash ...

  2. 11.redis cluster的hash slot算法和一致性 hash 算法、普通hash算法的介绍

    分布式寻址算法 hash 算法(大量缓存重建) 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡) redis cluster 的 hash slot 算法 一.hash 算法 来了一 ...

  3. 程序员代码面试指南 IT名企算法与数据结构题目最优解

    原文链接 这是一本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现.针对当前程序员面试缺乏权威题目汇总这一痛点,本书选取将近200道真实出现过的经典代码面试题,帮 ...

  4. 【数据结构与算法】一致性Hash算法及Java实践

    追求极致才能突破极限 一.案例背景 1.1 系统简介 首先看一下系统架构,方便解释: 页面给用户展示的功能就是,可以查看任何一台机器的某些属性(以下简称系统信息). 消息流程是,页面发起请求查看指定机 ...

  5. Hash算法和一致性Hash算法

    Hash算法 我们对同一个图片名称做相同的哈希计算时,得出的结果应该是不变的,如果我们有3台服务器,使用哈希后的结果对3求余,那么余数一定是0.1或者2,正好与我们之前的服务器编号相同,如果求余的结果 ...

  6. 【算法】一致性Hash算法

    一.分布式算法 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法( ...

  7. 程序员编程艺术:面试和算法心得-(转 July)

    1.1 旋转字符串 题目描述 给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdef ...

  8. 程序员的进阶课-架构师之路(14)-B+树、B*树

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/de ...

  9. 程序员的进阶课-架构师之路(13)-B-树

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/de ...

随机推荐

  1. 在ASP.NET Core中编写合格的中间件

    这篇文章探讨了让不同的请求去使用不同的中间件,那么我们应该如何配置ASP.NET Core中间件?其实中间件只是在ASP.NET Core中处理Web请求的管道.所有ASP.NET Core应用程序至 ...

  2. spring @Value("${name}")使用

    在springmvc.xml配置文件中加入 (注意是springmvc配置文件不是spring配置文件的xml)不然可能取不到值 <context:property-placeholder lo ...

  3. SpringBoot自定义starter及自动配置

    SpringBoot的核心就是自动配置,而支持自动配置的是一个个starter项目.除了官方已有的starter,用户自己也可以根据规则自定义自己的starter项目. 自定义starter条件 自动 ...

  4. 大前端技术系列:TWA技术+TensorFlow.js => 集成原生和AI功能的app

    大前端技术系列:TWA技术+TensorFlow.js => 集成原生和AI功能的app ( 本文内容为melodyWxy原作,git地址:https://github.com/melodyWx ...

  5. 虚拟机kali linux与windows主机共享文件

    在windows中我们可以直接拖拽文件,有的时候wmtools无效 很苦恼 从kali中托文件就不可以, 于是那就用到了共享文件夹 一.在虚拟机名称处右键,选择设置,会弹出一个设置框 二.选择选项-& ...

  6. iOS:探究视图控制器的转场动画

    一.介绍 在iOS开发中,转场动画的使用无处不见,不只是我们自己更多的使用UIViewblock动画实现一个转场动画,其实,在我们实现VC控制器跳转的时候都是转场动画的实现,例如标签栏控制器的切换.模 ...

  7. 精心整理(含图版)|你要的全拿走!(R数据分析,可视化,生信实战)

    本文首发于“生信补给站”公众号,https://mp.weixin.qq.com/s/ZEjaxDifNATeV8fO4krOIQ更多关于R语言,ggplot2绘图,生信分析的内容,敬请关注小号. 为 ...

  8. day 2 DP专场

    上午讲了一上午背包,从01背包到完全背包到多重背包,感觉也没说什么,旁边的大佬一直在飞鸽里说让老师讲快点,然而最后也没人敢跟老师说.... 例题真的各个都是神仙题, 挂饰 好像就是一上午最简单的... ...

  9. Salesforce学习之路-developer篇(五)Aura组件原理及常用属性

    很喜欢曾经看到的一句话:以输出倒逼输入.以输出的形式强制自己学习,确实是高效的学习方式,真的很棒.以下仅为个人学习理解,如有错误,欢迎指出,共同学习. 1. 什么是Lightning Componen ...

  10. LINUX 内核移植以及网卡驱动添加

    我用的板子是sama5d3xek,原来板子内核是linux-at91-3.13,升级使用linux-at91-4.10 首先去官网下载一个linux—at91-4.10压缩包,然后在ubuntu里解压 ...