python中集的'Bizarre'排序


14

当我将Python 3.8.0列表转换为集合时,结果集的排序*以一种很简单的方式高度结构化。如何从伪随机列表中提取此结构?


作为实验的一部分,我正在生成一个随机集合。我很惊讶地看到绘制该集合突然显示出该集合中意外的线性结构。因此,有两件事让我感到困惑-为什么转换为一组结果会导致排序*最终突出了这种结构;在较小程度上,为什么伪随机集完全具有这种“隐藏”结构?

编码:

X = [randrange(250) for i in range(30)]
print(X)
print(set(X))

例如输出

[238, 202, 245, 94, 111, 106, 148, 164, 154, 113, 128, 10, 196, 141, 69, 38, 106, 8, 40, 53, 160, 87, 85, 13, 38, 147, 204, 50, 162, 91]

{128, 8, 10, 141, 13, 147, 148, 154, 160, 162, 164, 38, 40, 50, 53, 196, 69, 202, 204, 85, 87, 91, 94, 106, 238, 111, 113, 245}

正如预期的那样,以上列表的图**看起来相当随机:

WolframAlpha图随机生成的列表

而绘制集合(按其在输出中的排序)则显示出集合中存在的结构:

来自随机列表的WolframAlpha图集

此行为在我的机器上(下面的更多示例)与上面的代码中使用的值250和30保持100%一致(我使用的示例不是精心挑选的-这只是我运行的最后一个)。调整这些值有时会导致结构略有不同(例如,三个算术级数的子集***而不是两个)。

在其他人的机器上可以复制吗?当然,这种结构的存在似乎表明不是那​​么好的伪随机数生成,但是这并不能解释在某种意义上如何转换为集合。据我所知,并没有正式的保证(从列表中转换时)集合的顺序是确定性的(即使是顺序排列,在后台也不会进行复杂的排序)。那怎么回事?


(*):我知道集合是无序集合,但我的意思是“有序集合”,即在调用print语句时,集合以某种顺序输出,从而始终突出显示底层集合结构。

(**):这些地块来自Wolfram Alpha。下面是两个示例:

在此处输入图片说明

(***):将随机数范围从250更改为500时的两个图:

在此处输入图片说明

Answers:


14

基本上,这是由于两件事:

  • Python中的集合是使用哈希表实现的,
  • 整数的哈希是整数本身。

因此,整数出现在基础数组中的索引将由整数值决定,该值与基础数组的长度取模。因此,将整数范围的整数放入集合中时,整数将倾向于保持升序:

>>> list(set(range(10000))) == list(range(10000))
True # this can't be an accident!

如果没有来自连续范围的所有数字,则“对基础数组的长度取模”部分将起作用:

>>> r = range(0, 50, 4)
>>> set(r)
{0, 32, 4, 36, 8, 40, 12, 44, 16, 48, 20, 24, 28}
>>> sorted(r, key=lambda x: x % 32)
[0, 32, 4, 36, 8, 40, 12, 44, 16, 48, 20, 24, 28]

如果您知道基础数组的长度以及添加元素的(确定性)算法,则该序列是可预测的。在这种情况下,数组的长度为32,因为它的初始长度为8,并且在添加元素时增加了三倍。

除了在末尾出现一个小提示(因为数字52和56不在集合中)之外,该范围分为两个序列,0, 4, 8, ...并且32, 36, 40, ...它们交替出现,因为哈希(这些数字本身的值)被取模32以进行选择数组中的索引。有碰撞;例如,4和36等于32的模,但是首先将4添加到集合中,因此36最终得出不同的索引。

这是此顺序的图表。图表中的结构只是一个嘈杂的版本,因为您是随机生成数字的,而不是一步一步生成的。

在此处输入图片说明

交错序列的数量将取决于集合的大小,该集合的大小与从其采样的范围的长度成比例,因为这确定了范围的长度以“哈希表”基础数组的长度为模的“环绕”多少次。这是一个具有三个交错序列的示例0, 6, 12, ...,66, 72, 78, ...并且36, 42, 48, ...:

>>> set(range(0, 90, 6))
{0, 66, 36, 6, 72, 42, 12, 78, 48, 18, 84, 54, 24, 60, 30}

啊! 这就解释了(也是很好的解释)!
— 约翰·唐

当然,图中的这种模式与集合中的基础结构无关(我们希望这种模式出现在带有随机列表的图中,如我的示例所示)...我只是被图例中的意外模式所吸引情节!
— 约翰·唐

您如何发现30是基础数组的长度?
— Mark Snyder

@MarkSnyder原来它是32,这意味着有冲突,但顺序是相同的,就好像它是模30
— kaya3

2
@MarkSnyder如果数组超过2/3满,它将被调整大小,因为如果让数组变满或接近满,则哈希表的性能将大大降低。
— kaya3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.