我正在尝试编写一个模拟Coupon collector's problem的程序。以下是该问题的快速参考:
如果有N张优惠券,你想用多少张优惠券
在每张优惠券至少抽出一次之前进行更换。
我想出了两条代码:
(一)

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

得到了10次collect(coupon)迭代的结果,平均值为:
[175. 151. 128. 132. 169. 118. 134. 138. 150. 135.]
143.0

(二)
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

以平均值运行collect(coupon)的10次迭代的结果是:
[184, 119, 286, 196, 172, 370, 163, 267, 238, 199]
219.4

我尝试了大量的交互,代码(1)和代码(2)产生了非常不同的结果。
我知道,收集所有50张优惠券的预期价值的正确答案是225,而代码(2)是正确的答案。另一方面,对于代码(1)失败的原因,我找不到合理的解释?为什么numpy.random.choice在本例中不起作用?

最佳答案

numpy问题(或不)
您的代码看起来很好,包括您使用的np.random.choice。您可能遇到的一个可能的混乱点与replace参数的默认值有关。replace默认为True,因此不需要在代码块(1)中将replace = True显式传递给choice
除了这个非常小的问题,你的代码没有明显的问题。因此,问题可能出在数学和概率上。
概率问题
225是绘图大小为1时的预期值。在代码(1)中,绘图大小随每次迭代而增加。这样想:随着抽奖规模的增加,在一次抽奖中获得所有优惠券的几率开始变得相当大。我不知道确切的数字(编辑:我找到了一些确切的数字。他们现在在下面的“深入调查”部分),但他们说,在一次100张的抽签中获得全部50张优惠券的概率是0.01。当你得到143的时候,至少所有的优惠券的累计赔率至少应该是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值的概率可以通过一个序列进行一次估计。现在,估计所有绘制尺寸的概率11-143,你可以运行:
n = 50
coupon = np.arange(0, n)
counts = collectx(coupon, np.arange(120,144), 100)

这将产生counts,它是一个形状(24, 100)的数组。绘制大小在行上变化,并且估计的复制ID在列上变化。你可以通过在估计中重复平均值并将结果分成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的绘制尺寸的概率很小,因此求出绘制尺寸11-143的概率的总和给出了对代码块(1)的累积概率的合理估计,在完成绘制尺寸143时,在一次绘制中已经看到了所有的优惠券:
print('%.3f' % probx.sum())

输出:
0.475

因此,当n==143时,您的代码块(1)很可能没有看到每个优惠券。这与你观察到的结果完全一致。所以没有核问题,只是概率问题。

10-07 22:46