34

我有一个列表,我使用 Python 内置的 shuffle 函数(random.shuffle

但是,Python 参考说明:

请注意,即使是相当小len(x)的 , x 的排列总数也大于大多数随机数生成器的周期;这意味着永远不会生成长序列的大多数排列。

现在,我想知道这个“相当小的 len(x)”是什么意思。100、1000、10000、...

4

3 回答 3

67

TL;DR:它在超过 2080 个元素的列表上“中断”,但不要太担心 :)

完整答案:

首先,请注意“洗牌”列表可以理解为(从概念上)生成列表元素的所有可能排列,并随机选择其中一个排列。

然后,您必须记住,所有独立的计算机化随机数生成器实际上都是“伪”随机的。也就是说,它们实际上并不是随机的,而是依靠一系列因素来尝试生成一个在高级中难以猜到或故意复制的数字。在这些因素中,通常是先前生成的数字。因此,在实践中,如果您连续使用随机生成器一定次数,您最终将重新开始获得相同的序列(这是文档所指的“周期”)。

最后,Lib/random.py(随机模块)上的文档字符串说“[随机数生成器]的周期是2**19937-1。”

因此,鉴于所有这些,如果您的列表存在2**19937或更多排列,则其中一些将永远无法通过重新排列列表获得。您将(再次在概念上)生成列表的所有排列,然后生成一个随机数 x,并选择第 x 个排列。下一次,您生成另一个随机数 y,并选择第 y 个排列。等等。但是,由于排列多于您获得的随机数(因为最多在2**19937-1生成数字之后,您将再次开始获得相同的排列),您将再次开始选择相同的排列。

所以,你看,这并不完全取决于你的名单有多长(尽管这确实进入了等式)。而且,2**19937-1是一个相当长的数字。但是,仍然,根据您的洗牌需求,您应该牢记所有这些。在一个简单的情况下(并且通过快速计算),对于没有重复元素的列表,2081 个元素将产生2081!排列,大于2**19937.

于 2010-06-17T15:15:31.840 回答
21

我最初在 Python 源代码中写了该评论,所以也许我可以澄清一下;-)

引入评论时,Python 的 Wichmann-Hill 生成器的周期要短得多,我们甚至无法生成一副纸牌的所有排列。

现在这个周期要大得多,2080 年对于当前的上限是正确的。可以加强文档以对此进行更多说明-但它们会变得非常乏味。

有一个非常简单的解释:周期 P 的 PRNG 有 P 个可能的起始状态。起始状态完全决定了产生的排列。因此,周期 P 的 PRNG 不能产生超过 P 个不同的排列(这是一个绝对上限 - 它可能无法实现)。这就是为什么要比较 N! 到 P 是这里的正确计算。而且,确实:

>>> math.factorial(2080) > 2**19937 - 1
False
>>> math.factorial(2081) > 2**19937 - 1
True
于 2013-09-05T04:53:08.997 回答
4

他们的意思是 n 个对象(记为 n!)上的排列增长得非常快。

基本上n!= nx n-1 x ... x 1;例如,5!= 5 x 4 x 3 x 2 x 1 = 120,这意味着有 120 种可能的方式来改组 5 项列表。

在同一个 Python 页面文档中,他们给出 2^19937-1 作为句点,即 4.something × 10^6001 或其他东西。根据关于阶乘的维基百科页面,我猜是 2000 年!应该在那个附近。(对不起,我没有找到确切的数字。)

所以基本上有很多可能的排列,洗牌会从中得到,可能没有真正的理由担心那些它不会的排列。

但如果这确实是个问题(讨厌的客户可能要求保证随机性?),您也可以将任务交给第三方;例如,请参见http://www.random.org/

于 2010-06-17T15:09:52.703 回答