CS61B_Hashcode

发布时间 2023-05-26 20:02:44作者: 哎呦_不想学习哟~

If we start with a hash table of size 2 and double when the load factor exceeds some constant. Why is this procedure for setting sizes suboptimal from the perspective of utilizing all of the bits of the hashCode?

这种哈希表大小设置的过程可能并不是从利用hashCode的所有位的角度来看最优的。这是因为如果我们始终以2的倍数增加哈希表的大小,我们实际上只在利用hashCode的最低几位。

在计算机科学中,当你在hashCode上进行模运算(例如 hashCode % tableSize)时,你实际上只在使用hashCode的最低几位来确定结果。如果tableSize始终是2的倍数,那么只有hashCode的最低几位会改变结果。这可能会导致哈希冲突率增加,因为大量不同的哈希码可能在低位数字上相同。

如果哈希表的大小不是2的幂,例如是素数,那么在执行模运算时,hashCode的更多位数将会被利用,从而降低哈希冲突的可能性。因此,尽管将哈希表大小设置为2的倍数在实现上可能更简单(因为乘以2只需位移运算即可),但这种方法可能无法充分利用所有hashCode的位。

最后,我们也需要注意到,在实践中,完全利用hashCode的所有位并不总是必要的。在许多情况下,只要哈希表的大小能够随着元素数量的增加而适当扩大,并且能够保持合理的负载因子,那么哈希冲突的问题可以得到有效控制。

 

好的,让我通过一个简单的例子来解释。

假设我们有一组hashCode为 001, 011, 101, 和 111(在二进制中)的数据。现在我们有一个大小为2的哈希表,那么我们实际上只能利用每个hashCode的最后一位,因为我们用hashCode对2进行模运算(即,hashCode % 2)来决定每个元素应该放在哪个位置。

1. 对于hashCode为001的元素,其最后一位是1,所以我们将其放在位置1。
2. 对于hashCode为011的元素,其最后一位也是1,所以我们同样将其放在位置1,这就产生了冲突。
3. 对于hashCode为101和111的元素,它们的最后一位也都是1,所以我们又将它们放在位置1,继续产生冲突。

现在假设我们将哈希表的大小增加到4(仍然是2的倍数)。此时我们可以利用每个hashCode的最后两位。

1. 对于hashCode为001的元素,其最后两位是01,所以我们将其放在位置1。
2. 对于hashCode为011的元素,其最后两位是11,所以我们将其放在位置3。
3. 对于hashCode为101的元素,其最后两位是01,所以我们将其放在位置1,这产生了冲突。
4. 对于hashCode为111的元素,其最后两位是11,所以我们同样将其放在位置3,这同样产生了冲突。

所以,即使我们扩大了哈希表的大小,只要大小仍然是2的倍数,我们依然可能会遇到哈希冲突的问题。这是因为我们仍然只能利用hashCode的最低几位。

然而,如果哈希表的大小是一个非2的倍数,比如3,那么在决定元素位置时就能利用到hashCode的更多位,从而降低哈希冲突的可能性。

 

如果哈希表的大小是2的倍数,那么只有输入的哈希码的低位才会影响数据存储的位置。这是因为当我们对一个数进行2的幂次运算时,结果实际上只取决于这个数的低位。换句话说,如果哈希表的大小始终是2的倍数,那么哈希函数实际上只会使用输入的低位,而不是全部位。

而如果哈希表的大小是一个素数,那么所有的哈希码位都会影响到数据存储的位置。这是因为素数的性质,使得不同的哈希码,即使在高位相似,也会有很大可能性在对哈希表大小取模后,落在不同的位置。这样就更有可能使所有的桶都被均匀地使用,降低哈希冲突的可能性。

让我们来举一个例子。假设哈希码是10位的二进制数,且哈希表的大小是2的10次方,即1024。这时候,无论哈希码的前几位是什么,只有哈希码的后10位会影响这个数据落在哪个桶里。因为如果一个数对1024取模,结果就等于这个数的后10位。但是,如果哈希表的大小是一个不是2的倍数的数,那么在确定桶位置时将使用hashCode的更多位。这是因为非2的幂数具有更强的“散列”属性,可以使hashCode的所有位都在确定桶位置时起作用。因此,如果哈希表的大小是一个素数或非2的幂数,那么它们就能更好地分散hashCode,从而减少哈希冲突。

例如,假设我们有一组hashCode:1001、1101、1011和1111。如果我们的哈希表大小为4(2的幂),那么在确定桶位置时只会使用每个hashCode的最后两位。这就导致所有的hashCode都被映射到了同一桶(桶1或桶3)中。但是,如果哈希表的大小为3(非2的幂),那么在确定桶位置时将使用每个hashCode的所有位。结果是这四个hashCode被映射到了不同的桶中,从而避免了哈希冲突。

 

 

为什么如果tableSize始终是2的倍数,无法充分利用所有hashCode的位。

当哈希表的大小是2的倍数时,我们通常使用hashCode与哈希表大小进行模运算来决定元素在哈希表中的位置。由于我们的哈希表大小始终是2的幂,所以在二进制形式下,哈希表大小总是形如100...000(一后面全是零)。

当我们用hashCode对此类数进行模运算时,结果完全取决于hashCode的低位。原因是这样的:假设我们有一个二进制数A=abcd(其中a,b,c和d代表位),我们将其模一个数B=100(二进制形式)。我们可以看到,无论A的高位a是多少,它都不会影响最后的结果,因为它对应的在B中是0。只有bcd这些位才会影响最后的结果。

所以,如果我们的哈希表大小始终是2的倍数,无论hashCode的高位是什么,都不会影响元素在哈希表中的位置,只有低位才会影响。这就意味着我们没有充分利用hashCode的所有位,可能导致一些不必要的哈希冲突。

然而,如果哈希表大小不是2的倍数,那么当我们用hashCode对其进行模运算时,hashCode的所有位都可能会影响结果。这样我们就能更好地利用hashCode的所有位,降低哈希冲突的概率。

 

 

结论: 不要使用2 的幂次方来作为hash table 的size