假设我有一个任意长度k
的Python列表。现在,假设我想要一个n
的随机样本,(其中n <= k!)该列表的 distinct 排列。我很想尝试:
import random
import itertools
k = 6
n = 10
mylist = list(range(0, k))
j = random.sample(list(itertools.permutations(mylist)), n)
for i in j:
print(i)
但是,自然地,当k
太大时,此代码将变得非常慢。鉴于我可能正在寻找的n
的排列数与排列总数相比相对较小,因此不需要计算所有排列。但重要的是,最终列表中的所有排列都不能重复。
您将如何更有效地实现这一目标?请记住,mylist
可以是任何东西的列表,为简单起见,我只是使用list(range(0, k))
。
答案 0 :(得分:2)
您可以生成排列,并跟踪已经生成的排列。为了使其更加通用,我制作了一个生成器函数:
import random
k = 6
n = 10
mylist = list(range(0, k))
def perm_generator(seq):
seen = set()
length = len(seq)
while True:
perm = tuple(random.sample(seq, length))
if perm not in seen:
seen.add(perm)
yield perm
rand_perms = perm_generator(mylist)
j = [next(rand_perms) for _ in range(n)]
for i in j:
print(i)
答案 1 :(得分:1)
下面是我做过的天真的实现(通过@ Tomothy32很好地实现,使用生成器的纯PSL):
import numpy as np
mylist = np.array(mylist)
perms = set()
for i in range(n): # (1) Draw N samples from permutations Universe U (#U = k!)
while True: # (2) Endless loop
perm = np.random.permutation(k) # (3) Generate a random permutation form U
key = tuple(perm)
if key not in perms: # (4) Check if permutation already has been drawn (hash table)
perms.update(key) # (5) Insert into set
break # (6) Break the endless loop
print(i, mylist[perm])
它依靠numpy.random.permutation
随机置换序列。
关键思想是:
tuple
的{{1}},因为它必须散列)以防止重复; 这个朴素的版本不会直接遭受itertools.permutations
函数的阶乘复杂度int
的影响,该函数会在从中采样之前生成所有O(k!)
排列。
关于算法设计和复杂性,有一些有趣的事情...
如果我们要确保循环可以结束,则必须强制执行k!
,但不能保证。此外,评估复杂度还需要知道在发现新的随机元组并将其破坏之前,无限循环实际上将循环多少次。
让我们封装@ Tomothy32编写的函数:
N <= k!
例如,此调用仅适用于很小的import math
def get_perms(seq, N=10):
rand_perms = perm_generator(mylist)
return [next(rand_perms) for _ in range(N)]
:
k<7
但是当get_perms(list(range(k)), math.factorial(k))
增长时,将在O(k!)
复杂性(时间和内存)之前失败,因为找到所有其他k
密钥后,它会归结为随机地找到唯一的丢失密钥。 / p>
另一方面,当k!-1
时,该方法似乎可以在合理的时间内生成合理数量的置换元组。例如,可以在不到一秒钟的时间内绘制出多个N<<<k!
个长度为N=5000
的元组,其中k
。
当10 < k < 1000
和k
保持较小且N
时,该算法似乎很复杂:
N<<<k!
; k
。这有点有价值。