我正在尝试编写一个模拟Coupon collector's problem的程序。这是该问题的快速参考:
给出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
得到10次collection(coupon)迭代的结果,平均值为:
[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
运行平均值的collect(coupon)10次迭代的结果是:
[184, 119, 286, 196, 172, 370, 163, 267, 238, 199]
219.4
我尝试了大量的交互,代码(1)和代码(2)产生了截然不同的结果。
我知道收集所有50张优惠券的期望值的正确答案是225,而代码(2)是正确的答案。另一方面,对于代码(1)为何失败,我找不到合理的解释。为什么在此示例中numpy.random.choice不起作用?
答案 0 :(得分:4)
您的代码似乎不错,包括您使用np.random.choice
。您可能遇到的一个混乱点可能与replace
参数的默认值有关。 replace
默认为True
,因此您无需在代码块(1)中将replace = True
显式传递给choice
。
除了这个非常小的问题之外,您的代码没有任何明显的问题。因此,问题可能出在数学和概率上。
225是绘制大小为1时的期望值。在代码(1)中,绘制大小随每次迭代而增加。这样考虑:随着抽奖规模的增加,在一次抽奖中获得所有优惠券的几率开始变得相当大。我不知道确切的数字(编辑:我找到了一些确切的数字。它们现在在下面的“更深入的调查”部分中),但是我说的是有可能在一个单次抽奖100是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)
这将产生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的抽奖,该概率仍然低于.005,但是它迅速增加,并且在143的抽奖中,看到每个优惠券的概率接近.05。由于抽奖大小低于120的概率很小,因此对抽奖大小120-143的概率求和可以合理估计代码块(1)的累积概率,该代码块在完成一次抽奖后已在一次抽奖中看到了所有优惠券绘制尺寸143:
print('%.3f' % probx.sum())
输出:
0.475
因此,n==143
时,您的代码块(1)可能没有看到所有优惠券。这与您观察到的结果完全一致。因此,没有Numpy问题,只是一个概率问题。