散列冲突与作为特征值的散列

简介:

缘起

写这篇文章,源于这么一个问题:假设目前有一千万个URL访问记录,请统计最热门的10个查询串。(见此文)。见到这个问题的第一想法使用hash解决,没考虑hash冲突解决的问题(其实就没想比较URL,不比较URL无法判断冲突与否)。后来意识到hash解法在内存受限情况下存在致命缺陷,才有写这个blog的想法。

散列/散列函数

Hash,一般翻译做散列,也音译为哈希,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法(散列函数),变换成固定(或不定)长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。散列函数(或散列算法,Hash Function)是一种从任何一种数据中创建小的数字指纹的方法。该函数将数据打乱混合,重新创建一个叫做散列值的指纹。

作为特征值的散列

散列函数是一种从任何一种数据中创建小的数字指纹的方法。密码学上的 Hash 又被称为"消息摘要(message digest)"简言之,指纹信息摘要本质就是数据的特征值,即散列函数可用于提取数据的特征值。在模式识别中,由于处理原始数据比较困难,经常通过提取数据的多维特征值来简化数据处理。

最佳的特征值是能完全表征数据的特征值,实际当中很难找到,不管是一维还是多维的特征值。完美散列就是希望能够找到能唯一表征原始数据的散列函数。

散列函数有一个基本特性:输出不同,原始数据必然不同;输出相同,原始数据可能不同。将无限定义域映射成有限值域必然存在冲突。加密散列函数是很不错的散列函数,其输出是一个很大的整数,值域很大,故冲突较少。

因为散列值无法唯一表征原始数据,故冲突判断只能通过比较原始数据方能得知。作为特征值的哈希值非常适合用于判断原始数据是否不同,比如文件、图片等。

MPQ散列函数

MPQ使用文件名哈希表来跟踪内部的所有文件。算法使用了3种不同的哈希:一个用于哈希表的下标,两个用于验证。这两个验证哈希替代了实际文件名。其本质就是使用3个哈希值来表征文件,当然,mpq无法解决散列冲突问题:即相同的3个哈希值对应两个不用的文件。

容忍冲突的散列(什么场合冲突可容忍?)

原始问题: 假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G

问题1将记录规模扩大1000倍,即几十亿的记录,内存几十G

问题2将记录规模扩大1000倍,即几十亿的记录,内存几G。即在内存受限的情况下,如何处理数据。(内存读写比硬盘快100倍以上,随机读写估计在1w倍以上,直接以磁盘空间代替内存空间的做法就不用提了)

对于问题1,想过一个算法:元数据存于内存+URL存磁盘。元数据:URL哈希值、访问计数,文件偏移量;所有的URL均存在磁盘中。这个算法有个前提:忽略冲突,将URL哈希值作为URL的唯一表征。元数据量不大((8+8+8)*1G=24G)可全部在内存中存放,处理很快;比较URL哈希值远比直接比较URL快;URL采用追加方式写入磁盘,效率也不是问题。但是忽略冲突却是一个致命的缺陷。

当然可以采取一些措施来减少哈希冲突,如采用128为哈希值,采用多维特征值(URL长度,双哈希值(可直接使用中间哈希值和最终哈希值)),但这些都无法保证无冲突,即不可能采用有限的特征来表示无限的URL空间!

分布式方案mapreduce

元问题:如果不能直接使用内存来处理所有的数据,那么问题12就退化成同样的问题:如何在内存受限的情况下处理大规模的数据?

这样的问题有通用解法:map-reduce。即先对数据分类(独立的类别),再分别处理,最终归并结果。

1) 将原始记录通过hash分别记录到不同的N个文件(同一个URL只出现在一个文件中);

2) 分别对N个文件做统计处理,排序输出;

3) 采用最小堆挑出最热门的m个记录。

经过这么分类之后,这个算法本质上是可并行处理的,很容易在分布式集群中实现。“分而治之”的办法真的很实用(怎么分很重要)Mapraduce的思想也很通用。

参考文献:

从头到尾彻底解析Hash 表算法

wiki散列

wiki散列函数

本文转自 zhenjing 博客园博客,原文链接:  http://www.cnblogs.com/zhenjing/archive/2011/06/09/hash.html  ,如需转载请自行联系原作者


相关文章
|
3月前
|
存储 算法 Java
【算法系列篇】哈希表
【算法系列篇】哈希表
|
1月前
|
存储 C++
哈希的开放定址法的实现【C++】
哈希的开放定址法的实现【C++】
|
6月前
|
存储 缓存 算法
一篇文章让你学会什么是哈希(上)
哈希概念 哈希在C++中有广泛的应用,它是一种用于快速查找和存储数据的数据结构和算法。以下是一些常见的哈希在C++中的应用: 哈希表(Hash Table):哈希表是一种高效的数据结构,用于存储键值对。在C++中,std::unordered_map 和 std::unordered_set 是标准库提供的哈希表实现。
|
4月前
|
存储 Serverless 测试技术
C++【初识哈希】
C++【初识哈希】
22 0
|
9月前
|
存储 Serverless C++
哈希(C++)上
哈希(C++)
59 0
|
4月前
|
存储 Serverless
不允许你还没有了解哈希表、哈希桶、哈希冲突的解决,如何避免冲突
不允许你还没有了解哈希表、哈希桶、哈希冲突的解决,如何避免冲突
40 0
|
9月前
|
存储 算法 编译器
【C++】哈希
unordered系列容器的使用、哈希表的底层结构及其模拟实现
|
9月前
|
存储 缓存 算法
趣味算法——探索哈希表的神秘世界
前言: 在编程世界中,数据存储和检索的效率常常是我们关注的重点。对于这个问题,哈希表提供了一个既高效又实用的解决方案。哈希表是一种通过哈希函数将键转化为数组索引,以实现快速查找的数据结构。在大多数情况下,哈希表能够在常数时间内完成查找,插入和删除操作,因此在许多应用场景中得到了广泛使用。
48 0
|
11月前
|
存储 算法 数据处理
哈希(散列)查找算法
本文主要是散列表的认识与哈希查找!
164 1
|
11月前
|
算法
散列,字符串hash初步
散列,字符串hash初步