列表的互斥随机抽样

3
input = ['beleriand','mordor','hithlum','eol','morgoth','melian','thingol']

我遇到了一个问题,需要创建X个大小为Y的列表,但是不能重复任何元素。

我一直在使用以下方法:

x = 3
y = 2

import random

output = random.sample(input, y)
# ['mordor', 'thingol']

但如果我重复这个动作,那么我就会得到重复的结果。

我希望输出的结果能够类似于:

[['mordor', 'thingol'], ['melian', 'hithlum'], ['beleriand', 'eol']]

因为我选择了大小为y = 2(每个列表有两个元素)的x = 3(三个列表)。

def random_generator(x,y):
    ....
3个回答

2
您可以简单地对原始列表进行洗牌,然后从中连续生成nm个元素。可能会有少于或多于该数量的组。请注意,input是Python内置函数的名称,因此我将其重命名为words
import itertools
from pprint import pprint
import random

def random_generator(seq, n, m):
    rand_seq = seq[:]  # make a copy to avoid changing input argument
    random.shuffle(rand_seq)
    lists = []
    limit = n-1
    for i,group in enumerate(itertools.izip(*([iter(rand_seq)]*m))):
        lists.append(group)
        if i == limit: break  # have enough
    return lists

words = ['beleriand', 'mordor', 'hithlum', 'eol', 'morgoth', 'melian', 'thingol']
pprint(random_generator(words, 3, 2))

输出:

[('mordor', 'hithlum'), ('thingol', 'melian'), ('morgoth', 'beleriand')]

更符合Python语言习惯的方式是通过迭代生成分组。可以通过将上述函数改为生成器,使其逐个生成每个分组,而不是返回一个相对较长的列表:

yield关键字可以帮助实现这一点。

def random_generator_iterator(seq, n, m):
    rand_seq = seq[:]
    random.shuffle(rand_seq)
    limit = n-1
    for i,group in enumerate(itertools.izip(*([iter(rand_seq)]*m))):
        yield group
        if i == limit: break

pprint([group for group in random_generator_iterator(words, 3, 2)])

1
您可以使用random.sampleitertools.grouper食谱相结合。
input = ['beleriand','mordor','hithlum','eol','morgoth','melian','thingol']
import itertools
import random
def grouper(iterable,group_size):
    return itertools.izip(*([iter(iterable)]*group_size))

def random_generator(x,y):
    k = x*y
    sample = random.sample(input,k)
    return list(grouper(sample,y))

print random_generator(3,2)
print random_generator(3,2)
print random_generator(3,2)
print random_generator(3,2)
print random_generator(3,2)
print random_generator(3,2)

对于一个运行,这将产生以下结果:
[('melian', 'mordor'), ('hithlum', 'eol'), ('thingol', 'morgoth')]
[('hithlum', 'thingol'), ('mordor', 'beleriand'), ('morgoth', 'eol')]
[('morgoth', 'beleriand'), ('melian', 'thingol'), ('hithlum', 'mordor')]
[('beleriand', 'thingol'), ('melian', 'hithlum'), ('eol', 'morgoth')]
[('mordor', 'hithlum'), ('eol', 'beleriand'), ('melian', 'morgoth')]
[('mordor', 'melian'), ('thingol', 'beleriand'), ('morgoth', 'eol')]

下一次运行:

[('mordor', 'thingol'), ('eol', 'hithlum'), ('melian', 'beleriand')]
[('eol', 'beleriand'), ('mordor', 'melian'), ('hithlum', 'thingol')]
[('hithlum', 'mordor'), ('thingol', 'morgoth'), ('melian', 'eol')]
[('morgoth', 'eol'), ('mordor', 'thingol'), ('melian', 'beleriand')]
[('melian', 'morgoth'), ('mordor', 'eol'), ('thingol', 'hithlum')]
[('mordor', 'morgoth'), ('hithlum', 'thingol'), ('eol', 'melian')]

2
为什么不直接打乱原始序列(或其副本),并将位置i:i+y的项作为组?从随机性的角度来看,有什么缺点吗? - Bakuriu

1
不要随机从列表中取两个元素,而是对列表进行随机化并遍历它,以创建您指定维度的新数组!
import random
my_input = ['beleriand','mordor','hithlum','eol','morgoth','melian','thingol']
def random_generator(array,x,y):
    random.shuffle(array)
    result = []
    count = 0
    while count < x:
        section = []
        y1 = y * count
        y2 = y * (count + 1)
        for i in range (y1,y2):
            section.append(array[i])
        result.append(section)
        count += 1
    return result
print random_generator(my_input,3,2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接