长度为n的Python随机布尔列表,恰好有k个True

6
假设我们想要一个包含n个0/1元素的列表,其中恰好有k个1。是否有一种比以下方法更简洁、更符合Python风格的一行式推导方式呢?
```python lst = [i for i in itertools.combinations(range(n), k)] lst = [[1 if j in indices else 0 for j in range(n)] for indices in lst] ```
def random_include(n, k):
    ret = []
    to_include = set(random.sample([i for i in range(n)], k))
    for i in range(n): 
        if i in to_include:
            ret.append(1)
        ret.append(0)
4个回答

7
这里有一个一行代码的解决方案。
output = sorted([1] * k + [0] * (n - k), key=lambda k: random.random())

@ChristianAichinger,我在答案中添加了一条注释来解决您的问题,尽管我也观察到OP没有要求加密级别的随机性。 - merlin2011
很抱歉对你这么苛刻,但是random.py是一种确定性随机数生成器,因此不适合进行加密工作。这关乎于你是否在意所有序列是否具有完全相同的概率。 - Christian Aichinger
玩弄你的解决方案一下,似乎可以得到正确的结果(尽管概率相等),很抱歉我对此大惊小怪。 - Christian Aichinger
真正的随机性?我不相信这种东西存在 :) - thefourtheye
@thefourtheye,非常公正的观点,我在使用那个术语时应该更加小心谨慎。由于Christian已经表示结果已经足以满足他,因此我已经删除了这个注释。 - merlin2011
显示剩余7条评论

5

使用random.shuffle文档):

random_list = [False] * j + [True] * k
random.shuffle(random_list)

将会给你一个随机列表,其中包含jFalsekTrue

需要注意的是实现自定义洗牌算法通常存在问题,因为很容易编写出产生看似随机结果但不提供恒定概率分布的算法。即某些序列比其他序列更可能出现!

这个问题在Jeff Atwood的博客文章中得到了广泛探讨。如果您想要正确洗牌的序列,正确的解决方案是使用Fisher-Yates算法,这正是random.shuffle()所负责执行的:

def shuffle(self, x, random=None, int=int):
    randbelow = self._randbelow
    for i in reversed(range(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = randbelow(i+1) if random is None else int(random() * (i+1))
        x[i], x[j] = x[j], x[i]

因此,最好依靠random.shuffle()而不是自己想出聪明的方法来进行洗牌!

感谢您对我的解决方案提供反馈。这只是出于好奇,但是为什么random.shuffle()使用random()函数的事实不会对Fisher-Yates算法造成问题呢?根据维基百科上关于该算法的文章,它似乎也存在PRNG问题。 - merlin2011
是的,具有小状态的伪随机数生成器会导致不均匀的概率分布。Python的随机模块(“Modules/_randommodule.c”)使用624个长整数= 19968个位的内部状态。 因此,在洗牌大约2000张牌时,它将达到其绝对极限(“math.log2(math.factorial(2083))”约为“2 ** 19968”)。 如果您要洗少于100-200个元素的序列,则应处于安全范围之内。 非常好的讨论,谢谢。 - Christian Aichinger

4
def random_include(n, k):
    my_list = [1] * k + [0] * (n - k)
    random.shuffle(my_list)
    return my_list

首先构建一个足够多 1 和 0 的列表。

my_list = [1] * k + [0] * (n - k)

然后将其随机排序。
random.shuffle(my_list)

0

尝试:

random.sample([1] * k + [0] * (n - k), n)

这将返回您想要的内容。


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