关于numpy.random.choice的工作原理

4
我正在尝试编写一个模拟收集优惠券问题的程序。以下是该问题的快速参考:
给定n个优惠券,你期望在有替换的情况下抽多少张优惠券才能至少抽到每种优惠券一次。
我提出了两段代码:
(1)
n = 50
coupon = np.arange(0, n)
def collect(coupon):
    number = 49
    collection = np.array([])
    while len(set(collection)) != n:
          number +=1
          collection = np.random.choice(coupon, replace = True, size = number)
    return number

使用collect(coupon)进行10次迭代后,得到的平均值为:

[175. 151. 128. 132. 169. 118. 134. 138. 150. 135.]
143.0

(2)

n = 50
coupon = np.arange(0,n)
def collect(coupon):
    collection = set()
    number = 0
    while len(collection) != n:
          number +=1
          got = np.random.choice(coupon)
          collection.add(got)
    return number

运行10次collect(coupon)并取平均值的结果为:

[184, 119, 286, 196, 172, 370, 163, 267, 238, 199]
219.4

我尝试了大量的迭代,代码(1)和代码(2)产生非常不同的结果。

我知道收集所有50个优惠券的期望值的正确答案是225,而代码(2)是正确的。另一方面,我找不到一个合理的解释为什么代码(1)失败了?为什么numpy.random.choice在这个例子中不起作用?


问题是为什么您期望(1)能够解决此任务或等于(2)。从统计学的角度来看,情况完全不同。它不是在可行的情况下绘制和附加,而是在绘图大小增长时独立绘制/不附加。这看起来像一个完全不同的随机过程,可能不容易正式分析。但正如您所看到的,经验上存在一个红旗。(编辑:Tel的答案包含一个可能相关的关键词:累积赔率 - sascha
1个回答

4

Numpy问题(或者不是问题)

您的代码看起来很好,包括您对 np.random.choice 的使用。您可能会遇到的一个混淆点与 replace 参数的默认值有关。 replace 默认为 True,因此在代码块(1)中,您不需要显式地传递 replace = Truechoice

除了这个非常小的问题外,您的代码没有明显的问题。因此,问题可能出在数学和概率上。

概率问题

当抽奖大小为1时,期望值为225。在您的代码(1)中,抽奖大小随着每次迭代而增加。就像这样思考:随着抽奖大小的增加,一次性获得所有优惠券的几率开始变得相当大。我不知道确切的数字(编辑:我找到了一些确切的数字。它们现在在下面的“更深入的调查”部分中),但假设在100次抽奖中一次性获得所有50张优惠券的概率为0.01。当您达到第143次抽奖时,至少一次获得所有优惠券的累积概率应该至少为0.4(很可能更高)。

更深入的调查

通过一些调整,您的代码可以用于估计在大小为x的单次抽奖中看到所有50张优惠券的概率:

def collectx(coupon, x, reps):
    x = np.asarray(x)
    n = coupon.size

    counts = np.zeros((x.size, reps), dtype=int)
    for i,xsub in enumerate(x):
        for j in range(reps):
            count = 1
            while np.unique(np.random.choice(coupon, size=xsub)).size < n:
                count += 1
            counts[i, j] = count

    return counts

可以通过传入一个序列来同时估计许多不同 x 值的概率。现在,要估计抽取大小在120-143之间所有可能性的概率,您可以运行以下命令:
n = 50
coupon = np.arange(0, n)
counts = collectx(coupon, np.arange(120,144), 100)

这将得到一个形状为(24,100)的数组counts,其中行数表示抽样大小的变化,列数表示估计值的复制编号的变化。通过对估计值复制进行平均并将结果除以1,您可以获得“在单次抽样中查看每个优惠券”的概率。
probx = (1/counts.mean(axis=1))

看起来像这样:

[0.00418971 0.00563    0.00661288 0.00694493 0.00690799 0.00854774
 0.00909339 0.01050531 0.01207875 0.01344086 0.01485222 0.0155642
 0.02004008 0.02115059 0.02015723 0.02377556 0.02639916 0.02379819
 0.02856327 0.03941663 0.04145937 0.03162555 0.03601008 0.04821601]

对于一个大小为120的抽奖,概率仍然低于0.005,但随着抽奖数量增加,概率迅速增加,当抽奖数量达到143时,看到每张优惠券的概率几乎为0.05。由于抽奖规模低于120的概率很小,因此对于抽奖规模在120-143之间的概率进行求和,可以合理估计您的代码块(1)在完成143次抽奖时是否看到所有优惠券的累积概率:

print('%.3f' % probx.sum())

输出:

0.475

因此,当n==143时,您的代码块(1)很有可能已经看到了所有的优惠券。这与您观察到的结果完全一致。因此,这不是Numpy的问题,而是概率问题。


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