在Python中随机选择所有组合的子集

bli*_*liu 4 python random combinations python-itertools

itertools.list(product([0, 1], repeat=n))当n很小时,我可以构造一个n长度二进制值组合的列表.

1000
0100
0110
1001
 .
 .
 .
Run Code Online (Sandbox Code Playgroud)

当n很大时,如何在不首先构建大量组合列表的情况下随机选择上面列表的子集?

假设我想在n = 30时随机选择100万个组合而不进行替换(总共2 ^ 30个组合)

我查看了itertools的扩展函数http://docs.python.org/2/library/itertools.html#recipes

def random_product(*args, **kwds):
    "Random selection from itertools.product(*args, **kwds)"
    pools = map(tuple, args) * kwds.get('repeat', 1)
    return tuple(random.choice(pool) for pool in pools)
Run Code Online (Sandbox Code Playgroud)

但它一次只返回一次.在获得100万个独特组合之前,我应该循环使用此功能吗?或者有更好的方法.谢谢!

Aar*_*ski 7

你可以用另一种方式思考这个问题.基本上你只需要0和之间的100万随机值2^30.

import random

num_selections = 1000000
range = 2 ** 30

def make_set(n, max):
  result = set()
  while(len(result) < n):
    rand = bin(random.randrange(max)) # converting to binary
    result.add(rand)
  return result

s = make_set(num_selections, range)
Run Code Online (Sandbox Code Playgroud)

这在我的机器上运行大约2秒钟.如果n大致相等,这种方法效率不高max.但是1000000 / (2^30) ~= 0.000931,它运作正常.

编辑:

@ user2285236的解决方案更简洁:

import random
random_group = random.sample(range(2**30), 10**6)
random_group = [bin(x) for x in random_group] # convert all to binary
Run Code Online (Sandbox Code Playgroud)

  • 或者只是`random.sample(范围(2**30),10**6)` (2认同)
  • 并不是的.random.sample的实现与您的实现非常相似,它可以从范围中采样而不会生成该范围内的所有值.试试`random.sample(range(2**62),2)`例如,你会立即得到一个结果.另一方面,`list(range(2**62))`会在我的16gb计算机上引发内存错误.random.sample docs提到了这个细节:"要从一系列整数中选择一个样本,请使用range()对象作为参数.这对于从大量人口中采样来说特别快且节省空间:样本(范围(10000000), K = 60)". (2认同)