【问题标题】:Selecting a random sample from a very large generator从一个非常大的生成器中选择一个随机样本
【发布时间】:2019-02-09 11:19:41
【问题描述】:

我正在尝试测试一些游戏策略,可以由 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

谁能想到另一种方法来做到这一点?

谢谢

【问题讨论】:

  • 分号...在python中...天哪。说真的,您的代码很难遵循。您没有遵循样式约定,也没有使用描述性很强的变量名称。 l 是您可以使用的最差变量名称的经典示例。使用类似found_set
  • @FHTMitchell 感谢您的反馈,仍然是初学者。代码大多是从别处偷来的,不管怎样。
  • 你试过this method吗?
  • @glibdud 我刚试过,效果很好,类似于下面 Eric Duminil 的回答。

标签: python random generator


【解决方案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)]

【讨论】:

  • 有趣的方法。您的方法返回的结果似乎比我的偏差更大。我不确定哪一个更接近统一。
  • 这很有趣......这〜应该〜是统一的,正如我链接的答案所吹捧的那样。我必须做更多的研究来检查
【解决方案2】:

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

【讨论】:

  • 非常感谢。这完美地工作。我想我们的想法是,我们不是选择随机解决方案,而是每行掷 100 次 10 面骰子?
  • @TryingNotToBeDumb:完全正确。
  • 我不认为掷 10 面骰子是最好的比喻。但这是一般的想法,得到问题的确定性答案,而不是尝试大量的组合
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-02
  • 2021-05-07
  • 2011-07-23
  • 2023-01-03
  • 2011-09-18
  • 1970-01-01
  • 2014-12-27
相关资源
最近更新 更多