从超大型发生器中选择随机样本

时间:2018-09-04 12:09:18

标签: python random generator

我正在尝试测试游戏的一些策略,这些策略可以由10个非负整数加起来等于100来定义。共有109个选择9,或者其中大约10 ^ 12个,因此比较它们并不是全部实际的。我想从其中随机抽取大约1,000,000个样本。

我已经尝试了the answers to this questionand this one中的方法,但是似乎所有方法都太慢而无法使用。最快的方法似乎在我的计算机上大约需要180个小时。

这就是我尝试制作发电机的方式(改编自先前的SE回答)。由于某些原因,更改prob似乎不会影响将其转换为列表的运行时间。

def tuples_sum_sample(nbval,total, prob, order=True) :
    """ 
        Generate all the tuples L of nbval positive or nul integer 
        such that sum(L)=total.
        The tuples may be ordered (decreasing order) or not
    """
    if nbval == 0 and total == 0 : yield tuple() ; raise StopIteration
    if nbval == 1 : yield (total,) ; raise StopIteration
    if total==0 : yield (0,)*nbval ; raise StopIteration
    for start in range(total,0,-1) :
        for qu in tuples_sum(nbval-1,total-start) :
            if qu[0]<=start :
                sol=(start,)+qu
                if order :
                    if random.random() <prob:
                        yield sol
                else :
                    l=set()
                    for p in permutations(sol,len(sol)) :
                        if p not in l :
                            l.add(p)
                            if random.random()<prob:
                                yield p

拒绝采样似乎需要大约300万年,因此也是如此。

randsample = []
while len(randsample)<1000000:
    x = (random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,100))
    if sum(x) == 100:
        randsample.append(x)
randsample

有人能想到另一种方法吗?

谢谢

2 个答案:

答案 0 :(得分:1)

几个具有挑战性的问题:

  • 您是否有任何理由必须生成整个种群,然后对该种群进行抽样?
  • 为什么需要检查您的数字总和是否为100?

您可以生成一组合计为一个值的数字。在这里查看第一个答案:

Random numbers that add to 100: Matlab

然后生成您想要的此类集合的数量(在这种情况下为1,000,000)。

import numpy as np

def set_sum(number=10, total=100):
    initial = np.random.random(number-1) * total

    sort_list = np.append(initial, [0, total]).astype(int)
    sort_list.sort()

    set_ = np.diff(sort_list)

    return set_

if __name__ == '__main__':
    import timeit

    a = set_sum()

    n = 1000000
    sample = [set_sum() for i in range(n)]

答案 1 :(得分:0)

Numpy来营救!

具体来说,您需要一个multinomial发行版:

import numpy as np
desired_sum = 100
n = 10
np.random.multinomial(desired_sum, np.ones(n)/n, size=1000000)

在几秒钟内输出一百万行包含10个随机整数的矩阵。每行总计为100。

这是一个较小的示例:

np.random.multinomial(desired_sum, np.ones(n)/n, size=10)

输出:

array([[ 8,  7, 12, 11, 11,  9,  9, 10, 11, 12],
       [ 7, 11,  8,  9,  9, 10, 11, 14, 11, 10],
       [ 6, 10, 11, 13,  8, 10, 14, 12,  9,  7],
       [ 6, 11,  6,  7,  8, 10,  8, 18, 13, 13],
       [ 7,  7, 13, 11,  9, 12, 13,  8,  8, 12],
       [10, 11, 13,  9,  6, 11,  7,  5, 14, 14],
       [12,  5,  9,  9, 10,  8,  8, 16,  9, 14],
       [14,  8, 14,  9, 11,  6, 10,  9, 11,  8],
       [12, 10, 12,  9, 12, 10,  7, 10,  8, 10],
       [10,  7, 10, 19,  8,  5, 11,  8,  8, 14]])

总和似乎正确:

sum(np.random.multinomial(desired_sum, np.ones(n)/n, size=10).T)
# array([100, 100, 100, 100, 100, 100, 100, 100, 100, 100])

仅Python

您还可以从10个零的列表开始,迭代100次并每次增加一个随机单元:

import random
desired_sum = 100
n = 10
row = [0] * n
for _ in range(desired_sum):
    row[random.randrange(n)] += 1

row
# [16, 7, 9, 7, 10, 11, 4, 19, 4, 13]
sum(row)
# 100