关于 numpy.random.choice 的工作原理
About how numpy.random.choice works
我正在尝试编写一个模拟 Coupon collector's problem 的程序。这是问题的快速参考:
Given n coupons, how many coupons do you expect you need to draw with
replacement before having drawn each coupon at least once.
我想出了两段代码:
(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 次 collect(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
运行 10 次 collect(coupon) 迭代的平均值的结果是:
[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) 中,绘制尺寸随着每次迭代而增加。这样想:随着抽奖规模的增加,在一次抽奖中获得所有优惠券的几率开始变得相当大。我不知道确切的数字(编辑:我找到了一些确切的数字。它们现在在下面的 "Deeper investigation" 部分),但是说得到所有的概率单次抽取 100 张中的 50 张优惠券是 0.01。当您抽到 143 时,获得所有优惠券至少一次的累积几率应该至少为 0.4(可能更大)。
深入调查
通过一些调整,您的代码可用于估计在单次抽奖中看到所有 50 张优惠券的几率 x:
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:
来获得 "see every coupon in a single draw" 概率
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
所以您的代码块 (1) 很可能在 n==143
时没有看到每张优惠券。这与您观察到的结果完全一致。所以没有 Numpy 问题,只是概率问题。
我正在尝试编写一个模拟 Coupon collector's problem 的程序。这是问题的快速参考:
Given n coupons, how many coupons do you expect you need to draw with replacement before having drawn each coupon at least once.
我想出了两段代码:
(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 次 collect(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
运行 10 次 collect(coupon) 迭代的平均值的结果是:
[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) 中,绘制尺寸随着每次迭代而增加。这样想:随着抽奖规模的增加,在一次抽奖中获得所有优惠券的几率开始变得相当大。我不知道确切的数字(编辑:我找到了一些确切的数字。它们现在在下面的 "Deeper investigation" 部分),但是说得到所有的概率单次抽取 100 张中的 50 张优惠券是 0.01。当您抽到 143 时,获得所有优惠券至少一次的累积几率应该至少为 0.4(可能更大)。
深入调查
通过一些调整,您的代码可用于估计在单次抽奖中看到所有 50 张优惠券的几率 x:
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 的抽奖规模,概率仍然低于 0.005,但它迅速增加,并且在 143 的抽奖规模下,看到每张优惠券的概率接近 0.05。由于抽奖规模低于 120 的概率很小,因此对抽奖规模 120-143 的概率求和可以合理估计代码块 (1) 在一次抽奖结束时看到所有优惠券的累积概率绘制尺寸 143:
print('%.3f' % probx.sum())
输出:
0.475
所以您的代码块 (1) 很可能在 n==143
时没有看到每张优惠券。这与您观察到的结果完全一致。所以没有 Numpy 问题,只是概率问题。