Hash函数及其应用

本文部分内容摘自网络,参考资料链接会在文后给出,在此感谢原作者的分享。

计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数据。用“人类”的语言描述,单向函数就是:如果某个函数在给定输入的时候,很容易计算出其结果来;而当给定结果的时候,很难计算出输入来,这就是单向函数。各种加密函数都可以被认为是单向函数的逼近。Hash函数(或者称为散列函数)也可以看成是单向函数的一个逼近。即它接近于满足单向函数的定义。

Hash函数还有另外的含义。实际中的Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。除此以外,Hash函数往往应用于查找上。所以,在考虑使用Hash函数之前,需要明白它的几个限制:

  • Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
  • 由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
  • 不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。

由于实现了Hash的数据结构支持随机读取(即直接定位,而不需要涉及各类查找算法),检索效率非常高,成为了很多存储引擎的首选,著名的有redis、memcache等,但是Hash的特性决定了一些应用场景下的不足:

  • Hash 索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询。
  • Hash 索引无法被用来避免数据的排序操作。(即Hash函数并不会自排序,相对的如B树,本身带有排序信息,在节点增删改时按规则维护)
  • Hash 索引不能利用部分索引键查询。

稍加扩展的话,我们还可以将Hash应用在各种数据分布式技术中,这方面说的比较多的是“一致性哈希算法”,著名的开源分布式NoSQL数据库系统Cassandra就应用了这一算法。

对于数据检索的低层面应用,主要是各类集合类型。在设计相关类型时,要考虑适当的Hash算法,考虑因素主要是以下几个方面:

  • 计算Hash值所需的时间。
  • Hash表长度。
  • Hash值分布情况。
  • 数据的查找频率。
  • Hash值冲突(重复)的概率。

冲突解决技术可分为两大类:开散列法(又称为链地址法)和闭散列法(又称为开发地址法)。可假设实现Hash结构时,数据存放在预先用数组实现的一片连续的地址空间,两种冲突解决技术的区别在于发生冲突的元素是存储在这片数组的空间之外(开散列法,一般为附加链表形式)还是空间之内(闭散列法)。与闭散列法相比,开散列法有如下优缺点:

  • 开散列法处理冲突无二次聚集现象,因此平均查找时间较短。
  • 由于开散列法中各链表上的节点空间是动态申请的,因此适合无法确定表长的情况。
  • 指针需要额外空间,故当记录规模较小时,闭散列法较为节省空间。
  • 在.NET中,链表的各个元素分散于托管堆各处,这会给垃圾回收带来压力,影响程序性能。

在C#中,实现了Hash函数的集合类我知道的有两个:System.Collections.Hashtable和System.Collections.Generic.Dictionary<TKey,TValue>,这两者区别如下:

  • Hashtable采用闭散列法来解决冲突,而Dictionary采用开散列法来解决冲突。
  • Hashtable在空间不够时,会自动扩容,在扩容时会重新计算所有元素的哈希码和哈希地址,会消耗大量时间进行计算,Dictionary不存在这个问题(自然Dictionary在空间不够时也要开辟新的空间,不过不需要重新计算和安排原有数据的哈希值和哈希地址,这方面内容可看Dictionary的源码便一清二楚了。
  • Hashtable的线程安全包含几个层次,默认可由多个读取器线程一个写入线程使用;若要允许多线程写入(在没有线程读取的情况下),则需要通过Synchronized方法返回的包装完成;如果使用一个或多个读取器以及一个或多个编写器,则Synchronized包装不提供线程安全的访问,此时应使用SyncRoot锁定集合。(MSDN说了这么多,然后告诉我说Hashtable是线程安全的,难道不是在玩我?)Dictionary没这么复杂,只要Lock(SyncRoot)即可。

ps:关于NoSql,文中涉及了若干NoSql数据库,博主就在此简单说下对NoSql的一些个人见解。现在NoSql可谓风生水起,恰如当年web2.0、ajax刚兴起的时候,其实都不是非常高深的技术,但却能打破传统,一领风骚好多年,所以说技术是其次,思想才是最重要的。ok扯远了,NoSql和Sql存储引擎差不多,总归就那么几种,文中说到的Hash是一种,B数是一种,还有LSM树之类的,顶多在局部稍作改进以适应场景。它们真正的区别在于,NoSql不必非常顾忌数据库范式的约束,从而极大提高了读写速度和扩展能力,比如写操作不care事务,在每秒写几万几十万的数据量下,光这点就能甩Sql几条街。可以说各类NoSql的争奇斗艳,其实都是以取消或部分取消数据库范式的约束为前提,看似很小的改变,能换来性能的巨大提升,当然这也伴随着数据冗余、安全性不高等Sqls深恶痛绝的问题。上帝总是公平的,任何事物都没有绝对的好坏,就看你把它们用在什么地方。

参考资料:

Hash函数的几种

一致性哈希算法应用及优化(最简洁明了的教程)

三种基本的存储引擎比较

NoSQL数据库探讨之一 - 为什么要用非关系数据库?

转载请注明本文出处:http://www.cnblogs.com/newton/p/4561273.html

时间: 2024-11-05 20:33:45

Hash函数及其应用的相关文章

Bloom filter的实现以及常用的hash函数

bloom filter利用时间换空间的思想,利用多个哈希函数,将一个元素的存在状态映射到多个bit中,特别是在网络环境中,BF具有广泛的用途,关键问题就是要减少false positive rate(可以设置参数来调节),扩展有 counting BF.这里选用的hash函数是表现较好的 BKDRHash , SDBMHash, DJBHash . Bloom-filter代码: bloom_filter.h #ifndef __BLOOM_FILTER_H__ #define __BLOOM

Hash函数的安全性

我们为了保证消息的完整性,引进了散列函数,那么散列函数会对安全正造成什么影响呢?这是需要好好研究一番的问题. 三个概念: 1.如果y<>x,且h(x)=h(y),则称为碰撞. 2.对于给定的x,要找到一个y满足y<>x,h(x)=h(y),在计算上不可行,称为弱无碰撞. 3.要找到任意一对数,x,y,y<>x,满足h(x)=h(y),在计算上不可行,则称为强无碰撞.(包含弱无碰撞) 假定:一个取整数的随机变量,服从1到n的随机分布,一个含有k个这种变量的集合,至少有一对

Hash表的hash函数,冲突解决方法有哪些

最常用的3个HASH函数: 1. 除法散列法:通过取k除以m的余数,来将关键字k映射到m个槽的某一个中去,即散列函数为: h(k) = k mod m 2. 乘法散列法:首先,用关键字k乘上常数A(0<A<1),并抽取kA的小数部分:然后,用m乘以这个值,再取结果的底(即整数部分).散列函数可表达为: h(k) = ⌊m(kA mod 1)⌋ 3.全域散列法(universal hashing) 解决冲突常用的两种方法: 1. 链接法(chaining):把散列到同一槽中的所有元素都存放在一个

常用hash函数对比分析(一)

主要目标:寻找一个hash函数,高效的支持64位整数运算,使得在速度.空间等效率相对其它函数函数较高,以及内部运算时32位整数运算. 测试了"RSHash","JSHash","PJWHash","ELFHash","BKDRHash","SDBMHash","DJBHash","DEKHash","BPHash","

&lt;Openssl下hash函数&gt;

hash函数:是不可逆的函数,它的输入可以是任意长度的字节流.它的输出是固定大小的,hash函数的作用就是给你的文件产生一个摘要,它是独一无二的. 例如:y=f(x) x代表输入  y代表输出   输入x求y容易  单输入y求x就难了 我们常见的hash函数MD5和SHA1 当然和有其他的比较少见. DM5 :通常为128个bits 16个字节 sha1:通常为160bits     一个字节8个bits 20个字节 [[email protected] ~]# md5sum /etc/pass

Bitset&lt;&gt;用于unordered container时的默认hash函数

自从c++11起,bitset用于unordered container,将会提供默认的hash函数. 在gcc中,相关代码如下: 01495 // DR 1182. 01496 /// std::hash specialization for bitset. 01497 template<size_t _Nb> 01498 struct hash<_GLIBCXX_STD_D::bitset<_Nb>> 01499 : public std::unary_functi

深入理解 hash 函数、HashMap、LinkedHashMap、TreeMap 【上】

前言 Map 是非常常用的一种数据接口.在 Java 中,提供了成熟的 Map 实现. 图 1 最主要的实现类有 Hashtable.HashMap.LinkedHashMap和 TreeMap.在 HashTable 的子类中,还有 Properties的实现.Properties 是专门读取配置文件的类,我们会在稍后介绍.这里首先值得关注的是 HashMap 和 HashTable 两套不同的实现,两者都实现了 Map 接口.从表面上看,并没有多大差别,但是在内部实现上却有些微小的细节. 首

字符串编码---hash函数的应用

之前就听说过有个叫做hash表的东西,这段时间在上信息论与编码,也接触了一些关于编码的概念,直到今天做百度之星的初赛的d题时,才第一次开始学并用hash 一开始我用的是mutimap和mutiset,先对字符串从小到大排序,再存进mutimap中,之后遍历mutimap的键,结果都超时了,代码如下: #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #includ

密码学hash函数-SHA256-512

[latexpage] Hash函数又称哈希函数.散列函数.杂凑函数.它是一种单向密码体制,即从一个从明文到密文的不可逆映射,只有加密过程,没有解密过程. Hash函数H将可变长度的数据块M作为输入,产生固定长度的Hash值h=H(M). 在安全应用中使用的Hash函数称为密码学Hash函数.(单向性).(抗碰撞性) 弱抗碰撞性:给定一个消息M,要找到另一个消息M',使得H(M)=H(M')很难. 强抗碰撞性:要找到两个随机明文M和M',使得H(M)=H(M')很难. Hash函数特点: 1.

使用Hash函数和MAC产生伪随机数

基于Hash函数的PRNG 流程非常类似于对称密码的CTR工作模式 算法的伪码如下 m = ⌈n/outlen⌉ data = V W = the null String for i = 1 to m wi = H(data) W = W || wi data = (data + 1) mod 2seedlen return leftmost n bits of W 说明: V是种子,seedlen为V的长度,n是需要的输出位数,H是Hash函数 对于诸如SHA-2等强密码学Hash算法,目前还