7.哈希

  哈希(Hash)又称散列,它是一个很常见的算法。在Java的HashMap数据结构中主要就利用了哈希。哈希算法包括了哈希函数和哈希表两部分。我们数组的特性可以知道,可以通过下标快速(O(1))的定位元素,同理在哈希表中我们可以通过键(哈希值)快速的定位某个值,这个哈希值的计算就是通过哈希函数(hash(key) = address )计算得出的。通过哈希值即能定位元素[address] = value,原理同数组类似。

最好的哈希函数当然是每个key值都能计算出唯一的哈希值,但往往可能存在不同的key值的哈希值,这就造成了冲突,评判一个哈希函数是否设计良好的两个方面:

  1.冲突少。

  2.计算快。

  下面给出几种常用的哈希函数,它们的背后都有一定的数学原理且经过大量实践,其数学原理不在这里探究。  

BKDR哈希函数(h = 31 * h + c)

  这个哈希函数被应用在Java的字符串哈希值计算。

//String#hashCode
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];    //BKDR 哈希函数,常数可以是131、1313、13131……
        }
        hash = h;
    }
    return h;
}

DJB2 哈希函数(h = h << 5 + h + c = h = 33 * h + c)

  ElasticSearch就利用了DJB2哈希函数对要索引文档的指定key进行哈希。

SDBM哈希函数(h = h << 6 + h << 16 - h + c = 65599 * h + c)

  在SDBM(一种简单的数据库引擎)中被应用。

  以上只是列举了三种哈希函数,我们做下试验,看看它们的冲突情况是怎么样的。

  Java

 1 package com.algorithm.hash;
 2
 3 import java.util.HashMap;
 4 import java.util.UUID;
 5
 6 /**
 7  * 三种哈希函数冲突数比较
 8  * Created by yulinfeng on 6/27/17.
 9  */
10 public class HashFunc {
11
12     public static void main(String[] args) {
13         int length = 1000000;   //100万字符串
14         //利用HashMap来计算冲突数,HashMap的键值不能重复所以length - map.size()即为冲突数
15         HashMap<String, String> bkdrMap = new HashMap<String, String>();
16         HashMap<String, String> djb2Map = new HashMap<String, String>();
17         HashMap<String, String> sdbmMap = new HashMap<String, String>();
18         getStr(length, bkdrMap, djb2Map, sdbmMap);
19         System.out.println("BKDR哈希函数100万字符串的冲突数:" + (length - bkdrMap.size()));
20         System.out.println("DJB2哈希函数100万字符串的冲突数:" + (length - djb2Map.size()));
21         System.out.println("SDBM哈希函数100万字符串的冲突数:" + (length - sdbmMap.size()));
22     }
23
24     /**
25      * 生成字符串,并计算冲突数
26      * @param length
27      * @param bkdrMap
28      * @param djb2Map
29      * @param sdbmMap
30      */
31     private static void getStr(int length, HashMap<String, String> bkdrMap, HashMap<String, String> djb2Map, HashMap<String, String> sdbmMap) {
32         for (int i = 0; i < length; i++) {
33             System.out.println(i);
34             String str = UUID.randomUUID().toString();
35             bkdrMap.put(String.valueOf(str.hashCode()), str);   //Java的String.hashCode就是BKDR哈希函数, h = 31 * h + c
36             djb2Map.put(djb2(str), str);    //DJB2哈希函数
37             sdbmMap.put(sdbm(str), str);    //SDBM哈希函数
38         }
39     }
40
41     /**
42      * djb2哈希函数
43      * @param str
44      * @return
45      */
46     private static String djb2(String str) {
47         int hash = 0;
48         for (int i = 0; i != str.length(); i++) {
49             hash = hash * 33 + str.charAt(i);    //h = h << 5 + h + c = h = 33 * h + c
50         }
51         return String.valueOf(hash);
52     }
53
54     /**
55      * sdbm哈希函数
56      * @param str
57      * @return
58      */
59     private static String sdbm(String str) {
60         int hash = 0;
61         for (int i = 0; i != str.length(); i++) {
62             hash = 65599 * hash + str.charAt(i);    //h = h << 6 + h << 16 - h + c = 65599 * h + c
63         }
64         return String.valueOf(hash);
65     }
66 }

  以下是10万、100万、200万的冲突数情况:

  反复试验实际上三种哈希函数的冲突数差不多。

  Python3

 1 import uuid
 2
 3 def hash_test(length, bkdrDic, djb2Dic, sdbmDic):
 4     for i in range(length):
 5         string = str(uuid.uuid1())  #基于时间戳
 6         bkdrDic[bkdr(string)] = string
 7         djb2Dic[djb2(string)] = string
 8         sdbmDic[sdbm(string)] = string
 9
10 #BDKR哈希函数
11 def bkdr(string):
12     hash = 0
13     for i in range(len(string)):
14         hash = 31 * hash + ord(string[i])   # h = 31 * h + c
15     return hash
16
17 #DJB2哈希函数
18 def djb2(string):
19     hash = 0
20     for i in range(len(string)):
21         hash = 33 * hash + ord(string[i])   # h = h << 5 + h + c
22     return hash
23
24 #SDBM哈希函数
25 def sdbm(string):
26     hash = 0
27     for i in range(len(string)):
28         hash = 65599 * hash + ord(string[i])    # h = h << 6 + h << 16 - h + c
29     return hash
30
31 length = 100
32 bkdrDic = dict() #bkdrDic = {}
33 djb2Dic = dict()
34 sdbmDic = dict()
35 hash_test(length, bkdrDic, djb2Dic, sdbmDic)
36 print("BKDR哈希函数100万字符串的冲突数:%d"%(length - len(bkdrDic)))
37 print("DJB2哈希函数100万字符串的冲突数:%d"%(length - len(djb2Dic)))
38 print("SDBM哈希函数100万字符串的冲突数:%d"%(length - len(sdbmDic)))

  哈希表是一种数据结构,它需要配合哈希函数使用,用于建立索引,便于快速查找——《算法笔记》。一般来讲它就是一个定长的存储空间,比如HashMap默认的哈希表就是定长为16的Entry数组。有了定长的存储空间过后,剩下的问题就是如何将值放入哪个位置,通常如果哈希值是m,长度为n,那么这个值就放到m mod n位置处。

  上图就是哈希和哈希表,以及产生冲突的解决办法(拉链法)。产生冲突后的解决办法有很多,有再哈希一次直到没有冲突,也有向上图一样采用拉链法利用链表将相同位置的元素串联。

  想象一下,上面的例子哈希表的长度为10,产生了1次冲突,如果哈希表长度为20,那么就不会产生冲突查找更快但会浪费更多空间,如果哈希表长度为2,将会倒置3次冲突查找更慢但这样又会节省不少空间。所以哈希表的长度选择至关重要,但同时也是一个重要的难题。

补充:

  哈希在很多方面有应用,例如在不同的值有不同的哈希值,但也可以将哈希算法设计精妙使得相似或相同的值有相似或相同的哈希值。也就是说如果两个对象完全不同,那么它们的哈希值也完全不同;如果两个对象完全相同,那么它们的哈希值也完全相同;两个对象越相似,那么它们的哈希值也就越相似。这实际上就是相似性问题,也就是说这个思想可以被推广应用到相似性的计算(例如Jaccard距离问题),最终应用到广告精准投放、商品推荐等。

  另外,一致性哈希也可应用在负载均衡,如何保证每台服务器能均匀的分摊负载压力,一个好的哈希算法也可做到。

时间: 2024-08-10 19:18:29

7.哈希的相关文章

NYOJ 2356: 哈希计划【模拟】

题目描述 众所周知,LLM的算法之所以菜,就是因为成天打游戏,最近LLM突然想玩<金庸群侠传X>,结果进去后各种被虐,LLM就开始研究这个游戏的代码,顺便还学会了一点点点点lua语言,然后就开始了伟大的改游戏代码之旅,然后LLM发现自己too young了,这个游戏把所有的文本都进行了哈希,如果自己改了代码或者剧情文本的话它哈希出来的值就会和原来的哈希值不一样......然后游戏就会打不开.....,现在LLM发现了文本的哈希函数,要求你写个程序,功能为: 输入一段字符串,输出一个哈希值 为了

哈希表

哈希表支持的一种最有效的检索方法:散列. 由于计算哈希值和在数组中进行索引都只消耗固定时间,因此哈希表的最大亮点在于他是一种运行时间在常量级的检索方法. 哈希表主要有两种: 1.链式哈希表:将数据存储在桶中的哈希表,每个桶里面都是一个链表,且链表的容量随着冲突的增大而增大.(换句话说就是如果有冲突,会在桶中的链表加上一个存储的值) 2.开地址哈希表:将数据存在表本身,而不是在桶中,他通过各种探查方法来避免冲突. 解决冲突: 不管在以上那种哈希表中,我们的目标是尽可能均匀的分配表中的元素.所以我们

Aizu 2784 Similarity of Subtrees(树哈希)

Similarity of Subtrees Define the depth of a node in a rooted tree by applying the following rules recursively: The depth of a root node is 0. The depths of child nodes whose parents are with depth d are d+1 . Let S(T,d) be the number of nodes of T w

8. 蛤蟆的数据结构进阶八哈希表相关概念

8. 蛤蟆的数据结构进阶八哈希表相关概念 本篇名言:"作家当然必须挣钱才能生活,写作,但是他决不应该为了挣钱而生活,写作.--马克思" 前些笔记我们学习了二叉树相关.现在我们来看下哈希表.这篇先来看下哈希表的相关概念 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47347273 1.  哈希表的概念 哈希表(HashTable)也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构.它通过把关键

哈希(4) - 求两个链表的交集(intersection)以及并集(union)

给定两个链表,求它们的交集以及并集.用于输出的list中的元素顺序可不予考虑. 例子: 输入下面两个链表: list1: 10->15->4->20 list2: 8->4->2->10 输出链表: 交集list: 4->10 并集list: 2->8->20->4->15->10 方法1 (简单方法) 可以参考链表系列中的"链表操作 - 求两个链表的交集(intersection)以及并集(union)" 方法2

哈希(5) - 检测数组A[]中是否存在元素对其和为x

给定一个包括n个数值的数组A[]以及另一个数字x,判断数组中是否存在一对元素,它们的和等于x. 方法1 (使用排序) 算法: hasArrayTwoCandidates (A[], arrSize, sum) 1) 对数组进行递增排序 2) 初始化已排序数组中的两个索引值 (a) 将最左侧的数组位置0做为第一个索引left = 0 (b) 将最右侧的数组位置做为第二个索引right = arrSize-1 3) Loop while left < right. (a) if (A[left] +

哈希与加密(一)

声明:本文源自对哈希(Hash)与加密(Encrypt)的基本原理.区别及工程应用的学习整理 1.哈希与加密的区别 (1)哈希:将目标文本转换成具有相同长度.不可逆的杂凑字符串(或称消息摘要): (2)加密:将目标文本转换成具有不同长度.可逆的密文. 实际上,使用相同的hash算法,不论目标文本有多长,得到的结果长度固定:加密算法往往与目标文本的长度成正比. 2.哈希与加密的数学基础 (1)哈希算法:R=H(S)是多对一映射,多个不同的S可以通过哈希算法H得到相同的R.所以就不存在一个逆映射,使

hdu 1280 前m大的数 哈希

前m大的数 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 10574    Accepted Submission(s): 3686 Problem Description 还记得Gardon给小希布置的那个作业么?(上次比赛的1005)其实小希已经找回了原来的那张数表,现在她想确认一下她的答案是否正确,但是整个的答案是很庞大的表,小

《我与希乐仑》第二弹

致徐敏: 如果你觉得我的这篇报道侵害了你和贵公司的权益,你可以上法院告我,但我说的都是事实,不怕你告,有事找我律师,谢谢! 我是希乐仑科技发展(上海)有限公司前员工,曾经为希乐仑立下汗马功劳.这公司从2014年2月份开始搞我,我去年的绩效是3.8/5.0,完全没有绩效问题.他们倒好,自从我查完我们公司某商业间谍之后,就给我穿小鞋,说我这个不好,那个拖延,这不是扯淡吗?公司在3月5日非法把我裁掉,而且直到现在还未支付我2月份工资,行吧,那我就不再沉默了,当我吃素的是吧!我现在把这件事情公之于众,望