初学哈希表的时候可能会有一个错觉,就是在关键码一定的情况下,长度越大,哈希表的冲突概率应该越小。的确,当关键码在一个大范围内 ,比如0-1000, 完 全 随 机 \color{green}完全随机 完全随机 分布的情况下,当我们把哈希表的长度从1开始递增,在这个过程中冲突的概率自然逐渐减小。
但是实际运用中,关键码的分布往往呈周期性。
在邓俊辉教授的《数据结构习题解析》第182页有这样一道习题:
邓公在解答中详细论述了发生冲突的原因,在此不再赘述。仅就其中画红圈部分的M/g发表一些自己的理解:
首先仔细审题,一共有M个(也就是散列表长度)关键码,这说明关键码的取值范围大概是0~M*T。
其次观察
图中与05冲突的散列码:29、53、77
对于其中任意一个,比如29来说,有 5%12 == 29 % 12,即 5 % 12 == (5 + 8 + 8 + 8) % 12,不难观察到,(8+8+8)% 12 = 0。即3 * 8 % 12 == 0,这里8是周期T,12是表长M,记3为k,要找有多少个关键码在这一点(05)冲突即是找有多少个k使k * T % M == 0成立。
问题进行到这里,先暂停一下,看另外一个问题。
要使a * b % c == 0,abc三个数字应该满足什么条件?
首先对a、b进行因式分解:
a = a1 * a2 * a3… b = b1 * b2 * b3…
然后将a的部分质因子跟b的部分质因子凑出来一个相乘得c的式子,如果能
凑出来,那么a * b % c == 0这个等式就成立,反之,不成立。
例如:3*8 % 12 = 3 * (2 * 2 * 2) % 12 = (3 * 2 * 2) * 2 %12
显然等于0
好了回到刚才的问题,有多少个k使k * T % M == 0成立
令g = gcd(M, T)。
如果T凑出的一个因子是g,那k只需要凑出一个M/g即可。那么符合这个条件的k有哪些的?没错就是(M/g, 2M/g, 3M/g…) 那总共有几个这样的k呢? M/(M/g) = g个。
那么如果T凑出的因子小于g呢?不妨设为Tg,(此时有Tg < g) 不难验证(然而还是稍微有一点难度)g是Tg的整数倍。那么符合这个条件的k有哪些呢?(M/Tg, 2M/Tg, 3M/Tg…)那这时有多少k呢?M/(M/Tg) = Tg个。同样不难验证,这个范围是(M/g, 2M/g, 3M/g…)的一个真子集。
要使这样一个k的取值范围尽量小,那么就应该使T和M的最大公约数尽可能的小。如果M和T互质,最大公约数为1,此时k只能取(M/1, 2M/1, 3M/1…),即M的倍数,才会使等式成立。
所以要想冲突率降低,只需M和T的最大公约数尽可能的小,即M为素数。