如何随机洗牌具有比PRNG期间更多排列的列表?

时间:2015-12-07 17:11:03

标签: python algorithm random

我有一个包含大约3900个元素的列表,我需要随机置换以生成统计分布。我环顾四周,发现这个Maximal Length of List to Shuffle with Python random.shuffle解释了Python中PRNG的周期是2**19937-1,这导致列表的最大长度为2080,然后才能生成全部可能的排列。我只生成300-1000个列表的排列,因此我不太可能产生重复的排列,但是,由于这产生了统计分布,我希望将所有可能的排列作为潜在样本。

2 个答案:

答案 0 :(得分:2)

有比MT更长的PRNG,但很难找到它们。

获得所有3090!组合,你需要40,905位的熵。那大概是5kb。你应该能够从random.org这样的地方抓取一大块字节,没有任何问题。要获得精确平衡,您必须添加一些并进行拒绝采样。即,一次抓取12位(0..4095),并拒绝高于当前循环索引的数字。这可能会增加所需的位数,但可能不会超过8kb。

答案 1 :(得分:1)

我同意@ user2357112它不太可能是一个真正的问题 - 但似乎你应该能够使用标准的random模块,使所有的排列至少成为可能。< / p>

你可以做一个分而治之的方法。使用初始种子将列表分成2个列表,每个列表大约2000个。此类分区的数量大约为C(4000,2000),大约为1.66 x 10^1202。这比周期少,这表明至少可以使用random.sample()生成所有此类分区。然后 - 重新设置随机数发生器并置换上半部分。然后 - 第二次重新训练并将下半场置换。也许在重新设定之前几乎没有时间延迟,因此您不会遇到涉及系统时钟分辨率的问题。您还可以尝试将初始列表随机分区为更大数量的较小列表。

在数学上,很容易看出,如果您将列表随机分区为子列表,以便每个分区具有相同的可能性,然后以这样的方式置换每个子列表,使得所有子列表排列具有相同的可能性,并将这些子列表排列粘合在一起为了得到一个完整列表排列,那么所有整列排列都是可能的。

这是一个实现:

import random, time

def permuted(items, pieces = 2):
    sublists = [[] for i in range(pieces)]
    for x in items:
        sublists[random.randint(0,pieces-1)].append(x)
    permutedList = []
    for i in range(pieces):
        time.sleep(0.01)
        random.seed()
        random.shuffle(sublists[i])
        permutedList.extend(sublists[i])
    return permutedList

我不确定time.sleep(0.01)是否真的需要。我担心的是,如果种植在一毫秒内发生,那么在某些系统上可能会使用相同的种子。

作为最后的评论,仅仅因为上述函数(适当选择pieces)不能通过简单的计数参数显示错过某些排列(比较排列的数量与初始状态的数量)这本身并不构成所有排列实际上都是可能的证明。这将需要对随机数生成器,播种它的散列函数以及混洗算法进行更详细的分析。