【问题标题】:Maximal Length of List to Shuffle with Python random.shuffle?使用 Python random.shuffle 随机播放列表的最大长度?
【发布时间】:2011-03-05 00:19:47
【问题描述】:

我有一个列表,我使用 Python 内置的随机播放功能 (random.shuffle) 进行随机播放

但是,Python 参考说明:

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

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

【问题讨论】:

    标签: python random shuffle


    【解决方案1】:

    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 多。

    【讨论】:

    • +1 很好地解释了主题和问题。恕我直言,这应该是公认的答案。哦,我会把 TD;DR 移到顶部,因为大多数人被正文吓到可能不会读那么远:-)。
    • @Johannes:你不必删除你的答案 :) 不过,谢谢!
    • @rdp:嗯,现在有点多余了:-)。你在解释方面做得更好。
    • 请注意少于 100 个!宇宙中的原子。 IIRC 的近似数字仅为 10^^70th。嗯是的。不要担心它到底有多随机。
    • 人们可能会想切换到 random.SystemRandom aka secrect.SystemRandom。但是,如果在洗牌期间没有新的熵到达 /dev/urandom,根据stackoverflow.com/questions/32139660/… 的接受答案,它的周期会更短
    【解决方案2】:

    我最初在 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
    

    【讨论】:

    • 感谢您提供详细信息。我认为 random.shuffle 的文档目前有点太稀疏了。
    【解决方案3】:

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

    基本上是 n! = n x 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/

    【讨论】:

    • 或约翰内斯所说的 2081。猜猜我那时离我不远了。
    • 我在 Wolfram|Alpha 中手动缩小范围,因为它不会只给我“x!> 2^19937-1”的结果。
    • 我通过快速循环测试“math.factorial(i) >= 2**19937”:)
    • @rbp:我真的应该开始为我最喜欢的脚本环境 (PowerShell) 提供一些更好的数学能力 :-)
    • 或者给它Python绑定,并使用Python的stdlib! ;)
    猜你喜欢
    • 2023-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-02
    • 2016-10-17
    • 1970-01-01
    • 2014-04-02
    • 1970-01-01
    相关资源
    最近更新 更多