Algorithm 生成1000000个随机排列的样本
我正在处理大量的整数置换。每个排列中的元素数为K。元素大小为1字节。我需要生成N个唯一的随机排列。Algorithm 生成1000000个随机排列的样本,algorithm,language-agnostic,permutation,combinatorics,random-sample,Algorithm,Language Agnostic,Permutation,Combinatorics,Random Sample,我正在处理大量的整数置换。每个排列中的元素数为K。元素大小为1字节。我需要生成N个唯一的随机排列。 限制条件:这里有一种方法 1) 生成前N个排列并将其存储在磁盘上 2) 然后对排列运行随机算法 您可以使用“分而治之”进行优化,只从磁盘中拾取前X个元素,然后将其随机化,然后在下一次迭代中选择下一个X元素,以此类推。。。然后合并结果 这里可能不需要磁盘。一种可能的解决方案是使用 将排列存储在磁盘上(按顺序写入),并在RAM中维护bloom过滤器。 一旦您生成一个置换-检查它是否存在于bloom筛选
限制条件:这里有一种方法 1) 生成前N个排列并将其存储在磁盘上 2) 然后对排列运行随机算法 您可以使用“分而治之”进行优化,只从磁盘中拾取前X个元素,然后将其随机化,然后在下一次迭代中选择下一个X元素,以此类推。。。然后合并结果
这里可能不需要磁盘。一种可能的解决方案是使用 将排列存储在磁盘上(按顺序写入),并在RAM中维护bloom过滤器。
一旦您生成一个置换-检查它是否存在于bloom筛选器中,如果bloom筛选器说它尚未写入磁盘-写入它,bloom筛选器不会出现误报。
但是,如果布鲁姆过滤器说它在磁盘上,则可能是错误的 如果bloom筛选器显示“排列已存在”,则您可以决定是否退出此候选项并转到下一个候选项,而无需检查它是否确实已在集合中,或者您可以搜索磁盘以查看它是否确实存在。 如果你选择了后面的,你应该考虑保持一个聪明的DS用于排列,如A或A。
Bloom过滤器在这里是完美的匹配-它们被设计为表示一个可扩展的集合,同时给出0个假阴性,这是这里最重要的事情。我有点晚了,但我想我有一个方法尚未显示 我记得有一个算法,给定所有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)))
考虑到
10!~=3e6
即对于K>~15
如果您使用适当的Fischer Yates或Knuth shuffle对K个项目的列表进行了一百万次洗牌,那么您很可能每次都会得到一次独特的洗牌
如果您可以将所有一百万个唯一排列保存在一个集合数据结构中的内存中,那么您可以洗牌K个项的列表,并将它们添加到集合中,直到拥有一百万个为止
下面是一些Python,它还显示了shuffle在为不同的K生成独特的perm方面有多好:
>>> 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
>>>
来自数学导入阶乘的>>
>>>从随机导入洗牌
>>>
>>>n=1000000
>>>对于范围(16,9,-1)内的k:
perms=set()
perm=列表(范围(k))
试验=0
而len(perms)>>
问题是否存在于RAM中,还是存在于速度上?一个小小的改进:为了避免重复第2步和第3步,请生成1.1N个排列,删除重复项,然后取第一个N。如果排列的数量小于N,则转到第2步。您可以始终(按顺序)编写排列到磁盘,然后使用针对磁盘优化的排序算法,但这将比在RAM上执行所有操作慢得多。@RobertHarvey:两者都有。我想到了RAM和速度之间的折衷。排列可以从它们的排名(谷歌排列排名)生成。如果你能有效地选择N个不同的随机整数,那么你可以简单地将整数解列得到排列。问题是我需要排列均匀分布。前N个排列不是均匀分布,这可以在随机化算法上完成。你第一步使用的是什么技术?我只是一个接一个地生成排列。为了生成一个排列,我采用身份排列并将其洗牌。不知道为什么在我回答这个问题2年后投票失败。这里面有错吗?我在这里写了更多的博客:
>>> 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
>>>