对码当歌,猿生几何?

c#的dictionary为什么在扩容时会以素数扩容

  今天在看dictionary时,看到他在扩容时会以大于其两倍的最近素数大小来扩容,就思考了一下。

  因为dictionary是基于hash表的,hash后转正数后会去取模,所以我的思路就转到了是否用素数取模更优越,记录一下我的推断过程:

  最终的桶的index = key%m = key - xm = key - f(key/m)m。 这里的 f 函数用于向下取整。可以看到,最终结果是等价于key - f(key/m)m的。那么我们就以进来的key = 2n(即2的倍数),桶为4来举例。那么结果为 2n - f(2n/4)*4,简化后是2(n - f(n/2)*2)。可以看到这其实就是n - f(n/2)*2的两倍,而n - f(n/2)*2则是key = n装进长度为2的桶的公式,因此可以知道在这种情况下,它退化为了长度为2的桶。而又因为key = n装进长度为2的桶可能为0或1,它的两倍也就是0或2,即这种情况下(key为2n,桶长为4),它只会将值hash到0,2这两个桶里,并不均匀。其他合数亦是如此,只要有公约数,就会退化,无法均匀分布。

  最终结论是,如果进来的数是真随机的,那么不管用什么取模都无所谓。但是考虑到大多时候,进来hash的数列并不是这样,因此使用素数作为桶大小能够让其均匀分布到各个桶。