哈希取余法、哈希表大小取质数的问题

  1. hashing | planetmath.org http://planetmath.org/node/33326
  2. good hash table primes | planetmath.org http://planetmath.org/goodhashtableprimes
  3. 哈希函数取余法除数为什么要取质数? - SegmentFault http://segmentfault.com/q/1010000000593741
  4. 为什么哈希函数取余法要避免2的幂? - SegmentFault http://segmentfault.com/q/1010000000593556
  5. 哈希表详解 - 承续缘的信仰 - 博客频道 - CSDN.NET http://blog.csdn.net/liangbopirates/article/details/9753599

  6. 以前一直好奇,为何哈希表的大小须要取质数,并且还要取最好远离2的幂次方的质数?
    任意一个数x mod m的值,与m的值应该没有什么关系吧。
    看了上面一些讲的,本身总结一下。

    第一个连接planetmath讲的hashing里面,我以为说的挺对的,哈希函数的实质,就是把一个键值空间为K的映射到hashtable的空间T中。
    hash函数的设置有两个主要的要求:
    1.hash的计算要快,效率要高,即hash的计算公式要比较简单,不要太复杂
    2.hash的冲突率要低
    这里它说了两种hash函数的构造方法,一个是除,一个是乘:h(k)=f(k)(modn);h(k)=⌊n⋅((f(k)⋅r)(mod1))⌋,r是0到1的数。
    虽然都想要那种可以one to one的完美hash函数,不过通常很难办到,因此就有hash冲突,hash冲突就是两个不一样的键值k1,k2,
    可是h(k1)=h(k2),这就形成冲突。
    这里咱们定义一个负载因子:l=t/h,其中t是表明hashtable中已经被填好的槽,h是hashtable的总槽数。
    很明显,当l接近于1的时候,冲突是不可避免的。
    为了不冲突的话,有三种解决方案:
    1.把hashtable的大小开的足够大,比键值空间K的范围大得多
    2.采用拉链法
    3.选择一个完美hash函数,是one to one的。
    考虑option 1,设hash表中实际被填满的槽为A,A是K的子集,则有|A|<c|T|,实验效果发现,c取1/2到3/4
    之间比较好。
    考虑option 2,拉链法就没有冲突不冲突了,若是冲突了,直接再申请一块内存空间存下来就行了。
    考虑option 3,所谓的完美one to one的hash函数,其实主要是由目标数据和可用内存空间两个一块儿决定的
    举个简单例子,h(k)=k确定是一个完美hash,不过你的内存是否是可以把它们都保存下来就不必定了。
    而后提了两种hash:
    1.接近完美hash的函数,能够把n个映射到n个,没有冲突,这个须要好好构造。
    2.可以直接进行排序的,若是k1<k2,就有h(k1)<h(k2),这样至关于节约了排序的时间消耗o(nlog(n)),当K
    小于T的时候是可行的,不过因为这样确定会在hash函数中增长额外的计算,这样无形中对hash的效率是有
    必定影响的。

    第二个连接主要是对hash表的大小取质数进行了一个简单的介绍。
    它提供了一个hash表大小取值list,list里面的书都有3个特色:
    1.都是质数
    2.都是前一个质数的2倍小一点点
    3.都是远离2的幂次方的数
    至于为何这么选的缘由
    第一个质数,从第三个和第四个连接里面看的话,我的以为滕亦飞的答案比较正确。其实若是键值的取值是均匀的,没有什么特色,当K<T的时候,其实无论T的取值是多少,都是最优的,几率都是平均的。不过若是当K>T了,那么就是他分析的那样了,只要是K,T的公因子的那个就会比较吃香,映射过去的就比较多。因此天然是选择质数,几率范围最大了。
    第二个要是2倍小一点点,主要是考虑到数据量的增大,那么,这么选取比较方便
    第三个就纯粹说的是allegedly,这样的实验效果比较好的缘故了。

    第三个连接里面说,单纯取余的哈希运算很糟糕。“远离2^i的质数”、“接近2^i的质数”、“刚好等于2^i”都只不过是“没有那么差”、“确实不好”和“差到不能再差”的区别。
    我的以为说得不对,仍是根据以前的分析,若是说由于电脑是由二进制来表明数字,就说2的幂次方不适合作哈希表的大小,或者说效果不好,缘由不是彻底不是由于是二进制,2的幂次方,而是由于他是合数,而且数据自己分布不均匀致使的。
    若是数据分布均匀,其实不论是2的幂次方仍是3的幂次方都会是同样的结果。
    还有就是第四个连接里面说的,感受一部分说的挺对的。
    不过里面也说,首先不能选择2的倍数,缘由是这样至关于把数字的高位截断了,若是数字恰巧只有高位部分,那就悲剧了。从某种程度上说,感受说得挺对的,不过和他本身以前的理论就有点儿矛盾。原本他本身就说选啥都OK,结果如今又说选2的幂次方有问题,这个我不能接受。
    我以为根本性的问题仍是在于他自己是一个质数上。
    由于所谓的高位截断,其实就是说若是选择的是m大小的,那么k*m的都会被映射到同一个位中,这是很正常的一件事,由于你不能说计算机选择什么进制就不能以该数的幂次方来做为哈希表大小。
    固然我认可,单纯取余的哈希运算很糟糕,可是做为映射到哈希表中的最后一步填槽运算的时候,其实影响不大了,可能就和连接1,2里面说的那样,只要是个质数就行,只不过实验效果来看,原理2的幂次方的质数效果稍微好点儿。
    其实可能还有一个理由,有些哈希函数对字符串处理的时候,采用的就是ANSI编码或者什么编码方式,这种编码方式的话,可能弄出来的字符串转成整数时,出现2的幂次方的可能性比较大,数据分布很不均匀致使的。

    综上所述:
    1.单纯取余哈希算法效果很差的缘由主要是由于数据自己分布不均匀致使的,在数据分布均匀的状况下,无论你取的是质数仍是合数,对于最后的结果都没有影响,只要你的数据是平均分布在0-k*m的区间范围之间的。
    2.不过一个好的哈希函数,就是须要在各类不一样的数据分布状况下,其效果是最优的。考虑到这一点,哈希取余算法天然最好是一个质数,由于根据连接四滕亦飞里面他推导的公式,能够知道,做为质数,最后求出来的值的值域最大,在0-m-1之间
    3.由2可知,2的幂次方就首先被排除了,不只是2的幂次方、3的幂次方,4的幂次方……都被排除了。可是,为何不能取距离2的幂次方的质数、远离2的幂次方的质数,这一点,我认为是没有理论依据的,只能说,根据实验效果来看,远离2的幂次方的质数效果比较好,因此选择这个。
    4.上面3点说的都是做为哈希函数的取余算法的效果,然而做为使用哈希表的,无论你以前作了什么哈希函数的映射,到最后一步,存到哈希表中的时候,都会对哈希表的大小进行一个取余的运算,使数据尽可能均匀分布在槽里面。由上面说的,若是你以前的哈希函数足够好,把数据量都均匀分布了,那选啥都没问题;但为了防止以前的哈希不够好,天然哈希表的大小开具为一个质数比较OK,并且根据实验效果来看,开具成一个尽可能远离2的幂次方的哈希表大小比较OK。

若是有说错的地方欢迎你们批评指正!php

相关文章
相关标签/搜索