为什么tuple(set())== tuple(set())85%的时间哈希随机化启用?

给零比雷埃夫斯的另一个问题的答案 ,我们有这个

x = tuple(set([1, "a", "b", "c", "z", "f"])) y = tuple(set(["a", "b", "c", "z", "f", 1])) print(x == y) 

大约85%的时间打印True 哈希随机化启用。 为什么85%?

我会假设这个问题的任何读者都阅读了两个:

  • 零比雷埃夫斯的答案和

  • 我对CPython词典的解释

首先要注意的是散列随机化决定于解释器启动。

每个字母的散列对于两个集合都是相同的,所以唯一可能影响的是如果发生碰撞(订单将受到影响)。


通过扣除第二个链接,我们知道这些集合的支持数组开始于8:

 _ _ _ _ _ _ _ _ 

在第一种情况下,我们插入1

 _ 1 _ _ _ _ _ _ 

然后插入其余部分:

 α 1 ? ? ? ? ? ? 

然后它被重新大小32:

  1 can't collide with α as α is an even hash ↓ so 1 is inserted at slot 1 first ? 1 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 

在第二种情况下,我们插入其余的:

 ? β ? ? ? ? ? ? 

然后尝试插入1:

  Try to insert 1 here, but will ↓ be rehashed if β exists ? β ? ? ? ? ? ? 

然后它会被重新表示:

  Try to insert 1 here, but will be rehashed if β exists and has ↓ not rehashed somewhere else ? β ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 

所以迭代次序是否不同,完全取决于β是否存在。


β的机会是这5个字母中的任何一个将散列到1模8 散列到1模32的机会。

由于任何哈希到1的模32也哈希到1模8,所以我们想要find32个时隙中的一个,其中一个在时隙1:

 5 (number of letters) / 32 (number of slots) 

5/32为0.15625,所以两套结构中有15.625%的订单机会不同


不是很奇怪,这正是零比雷埃夫斯所测量的。


¹在技术上这还不是很明显。 我们可以假设5个哈希值中的每一个都是唯一的,因为重新哈希,但是由于线性探测,它实际上更有可能发生“聚束”结构……但是因为我们只关注是否占用一个时隙,实际上并没有影响到我们。