生成1,000,000个随机排列的样本

时间:2012-10-14 16:57:10

标签: algorithm language-agnostic permutation combinatorics random-sample

我正在处理大量的整数排列。每个排列中的元素数量为K.元素大小为1个字节。我需要生成N个唯一的随机排列 约束:K <= 144,N <= 1,000,000。

我想出了以下简单的算法:

  1. 生成N个随机排列的列表。将所有排列存储在RAM中。
  2. 对列表进行排序并删除所有重复项(如果有)。重复数量相对较少。
  3. 如果有任何重复项,请将随机排列添加到列表中,直到有N个排列并返回到第2步。
  4. 有更好的方法吗?特别是,有没有办法不将所有排列存储在RAM中(在生成时将它们写在磁盘上)?

    编辑:最后,需要按顺序访问生成的排列(逐个访问,不需要随机访问)。 RAM是更关键的因素(我宁愿不在RAM中同时存储所有排列)。

4 个答案:

答案 0 :(得分:6)

一种可能的解决方案是使用 bloom filters

将您的排列存储在磁盘上(按顺序写入)并在RAM中维护Bloom过滤器。
一旦你产生了一个排列 - 检查它是否存在于布隆过滤器中,如果布隆过滤器说它没有被写入磁盘 - 写入它,布隆过滤器没有假阴性。
如果布隆过滤器说它在磁盘上 - 可能是错误的。

如果bloom过滤器说“排列已经存在”,你可以决定是否要退出这个候选者并转到下一个候选者而不检查它是否真的已经在集合中,或者你可以搜索磁盘看到如果它真的存在
如果您选择更晚的版本,则应考虑为hash tableB+ tree等排列维护智能DS。

布隆过滤器在这里是完美的匹配 - 它们被设计成代表一个扩展阅读的集合,同时给出0个假阴性,这是最重要的事情。

答案 1 :(得分:3)

我有点晚了,但我想我的方法没有显示。

我记得他们是一个算法,给出了所有K项的起始顺序和一个整数索引,它将生成K项的索引排列大致与K成比例的时间。知道他们的K! (K)项的(阶乘)排列,只要你可以随机生成一个介于0和K之间的整数!您可以使用例程在内存中生成N个唯一随机索引,然后将相应的排列打印到磁盘。

这是算法的Python版本,N设置为10,k设置为25,尽管我已成功使用k = 144:

from math import factorial
from copy import copy
import random

def perm_at_index(items, index):
    '''
    >>> for i in range(10):
            print i, perm_at_index([1,2,3], i)


    0 [1, 2, 3]
    1 [1, 3, 2]
    2 [2, 1, 3]
    3 [2, 3, 1]
    4 [3, 1, 2]
    5 [3, 2, 1]
    6 [1, 2, 3]
    7 [1, 3, 2]
    8 [2, 1, 3]
    9 [2, 3, 1]
    '''

    itms, perm = items[:], []
    itmspop, lenitms, permappend = itms.pop, len(itms), perm.append
    thisfact = factorial(lenitms)
    thisindex = index % thisfact
    while itms:
        thisfact /= lenitms
        thischoice, thisindex = divmod(thisindex, thisfact)
        permappend(itmspop(thischoice))
        lenitms -= 1
    return perm

if __name__ == '__main__':
    N = 10      # Change to 1 million
    k = 25      # Change to 144
    K = ['K%03i' % j for j in range(k)] # ['K000', 'K001', 'K002', 'K003', ...]
    maxperm = factorial(k)              # You need arbitrary length integers for this!
    indices = set(random.randint(0, maxperm) for r in range(N))
    while len(indices) < N:
        indices |= set(random.randint(0, maxperm) for r in range(N - len(indices)))
    for index in indices:
        print (' '.join(perm_at_index(K, index)))

其输出看起来像这样:

K008 K016 K024 K014 K003 K007 K015 K018 K009 K006 K021 K012 K017 K013 K022 K020 K005 K000 K010 K001 K011 K002 K019 K004 K023
K006 K001 K023 K008 K004 K017 K015 K009 K021 K020 K013 K000 K012 K014 K016 K002 K022 K007 K005 K018 K010 K019 K011 K003 K024
K004 K017 K008 K002 K009 K020 K001 K019 K018 K013 K000 K005 K023 K014 K021 K015 K010 K012 K016 K003 K024 K022 K011 K006 K007
K023 K013 K016 K022 K014 K024 K011 K019 K001 K004 K010 K017 K018 K002 K000 K008 K006 K009 K003 K021 K005 K020 K012 K015 K007
K007 K001 K013 K003 K023 K022 K016 K017 K014 K018 K020 K015 K006 K004 K011 K009 K000 K012 K002 K024 K008 K021 K005 K010 K019
K002 K023 K004 K005 K024 K001 K006 K007 K014 K021 K015 K012 K022 K013 K020 K011 K008 K003 K017 K016 K019 K010 K009 K000 K018
K001 K004 K007 K024 K011 K022 K017 K023 K002 K003 K006 K021 K010 K014 K013 K020 K012 K016 K019 K000 K015 K008 K018 K009 K005
K009 K003 K010 K008 K020 K024 K007 K018 K023 K013 K001 K019 K006 K002 K016 K000 K004 K017 K014 K011 K022 K021 K012 K005 K015
K006 K009 K018 K010 K015 K016 K011 K008 K001 K013 K003 K004 K002 K005 K022 K020 K021 K017 K000 K019 K024 K012 K023 K014 K007
K017 K006 K010 K015 K018 K004 K000 K022 K024 K020 K014 K001 K023 K016 K005 K011 K002 K007 K009 K013 K019 K012 K021 K003 K008

答案 2 :(得分:0)

这是一种做法。

1)生成前N个排列并将它们存储在磁盘上。

2)然后对排列运行随机算法。

您可以使用Divide and Conquer进行优化,只需从磁盘中挑选第一个X元素,然后将其随机化,然后在下一次迭代中使用下一个X元素,依此类推......然后合并结果。

你可能不需要这里的磁盘。

答案 3 :(得分:0)

鉴于10! ~= 3e6K > ~15,如果您使用适当的Fischer-Yates或Knuth shuffle将K项目列表混乱一百万次,那么您很可能每次都获得一次独特的随机播放。

如果您可以在设置数据结构中保存内存中的所有一百万个唯一排列,那么您可以随机播放一个K项目列表并将它们添加到集合中,直到您有一百万个。

这里有一些Python,它也显示了一个衡量shuffle在为不同的K&s生成独特权限时有多好:

>>> from math import factorial
>>> from random import shuffle
>>> 
>>> n = 1000000
>>> for k in range(16, 9, -1):
    perms = set()
    perm = list(range(k))
    trials = 0
    while len(perms) < n:
        trials += 1
        for i in range(n - len(perms)):
            shuffle(perm)
            perms.add(tuple(perm))
    print('N=%i, K=%i, trials=%i, K!//N= %i' % (n, k, trials, factorial(k)//n))


N=1000000, K=16, trials=1, K!//N= 20922789
N=1000000, K=15, trials=1, K!//N= 1307674
N=1000000, K=14, trials=2, K!//N= 87178
N=1000000, K=13, trials=2, K!//N= 6227
N=1000000, K=12, trials=3, K!//N= 479
N=1000000, K=11, trials=5, K!//N= 39
N=1000000, K=10, trials=11, K!//N= 3
>>>