为什么runif()函数不能预测区间最大值?

11

我正在Reddit AskScience回答一个问题,与runif()的功能相关的事情让我感到奇怪。我试图从1到52中均匀地抽取一组样本。我的第一个想法是使用runif():

as.integer(runif(n, min = 1, max = 52))

然而,我发现这个操作从未产生过值为52的结果。例如:
length(unique(as.integer(runif(1000000, 1, 52))))
[1] 51

针对我的需求,我只是转而使用sample()

sample(52, n, replace = TRUE)

在runif()的文档中,它声明:
runif不会生成极端值,除非max = min或max-min与min相比较小,特别是对于默认参数不会生成。
我想知道为什么runif()会这样。如果它试图均匀生成样本,似乎应该能够从集合中产生“极端值”。这是一个特性,为什么?

1
获得恰好52的概率是0。为什么你会对没有观察到先前预设的概率为0的事件感到惊讶呢? - John Coleman
1
均匀分布是连续的 - 在连续分布中抽样任何给定值的概率都为零,只有在分布的区间上才有有限概率 - 因此,即使没有实现问题,你也很难最终抽样到52.000000… - Marius
1
请参阅此讨论:https://stats.stackexchange.com/questions/27431/uniform-distribution-generation-of-extreme-values-in-r - acylam
我在进行抽样的初次尝试中肯定犯了一些错误。这让我想到一个更有趣的问题,那就是为什么runif()函数不能产生极端值。Ben Bolker给出了很好的答案。 - user2059737
1
一个与 runif 兼容的替代方法是 ceiling(runif(100, min = 0, max = 52)) - lmo
3个回答

13

这确实是一个特性。 runifC源代码包含以下的C代码:

/* This is true of all builtin generators, but protect against
       user-supplied ones */
    do {u = unif_rand();} while (u <= 0 || u >= 1);
return a + (b - a) * u;

这意味着unif_rand()可能返回0或1,但是runif()的设计是跳过那些(不太可能的)情况。

我猜这样做是为了保护用户代码,以防在边界值上出现失败的情况。

该功能是由Brian Ripley于2006年9月19日实现的(从注释中可以看出,对于内置的均匀生成器,自动满足0<u<1,但对于用户提供的生成器可能不成立)。

sample(1:52,size=n,replace=TRUE)是实现您目标的惯用方式(虽然不一定是最有效的)。


3
as.integer 的作用类似于 trunc。它将通过向 0 截断给定的值来形成一个整数。由于值不能超过 52 (请参见 Ben 的答案),因此它们总是被截断为介于 1 和 51 之间的值。
使用 floor (或ceiling) 将得到不同的结果。请注意您必须通过添加 1 来调整 runifmax(或在使用 ceiling 的情况下调整 min)。此外,请注意在这种情况下,由于 minmax 都大于 0,您也可以用 truncas.integer 替换 floor
set.seed(42)
x = floor(runif(n = 1000000, min = 1, max = 52 + 1))
plot(prop.table(table(x)), las = 2, cex.axis = 0.75)

enter image description here


是的。谢谢。我在使用as.integer()时犯了错误,这让我想到了更有趣的问题。 - user2059737
如果您希望在整数范围内实现统一分布,则会出现问题:prop.table(table(round(runif(100000,1,10)))) 表明(例如)只有两个极端值的机会减少了一半。 - Ben Bolker
经进一步调查,@BenBolker,似乎使用适当调整的 floorceilingrunif 中的 minmax 可能更好:plot(prop.table(table(floor(runif(10000, 1, 52+1)))), las = 2) - d.b

2

as.integer(51.999)

51

这是因为 as.integer 的工作方式。

如果你想从离散分布中抽取样本,那么请使用 sample。而 runif 不适用于离散分布。


?runif states that the function itself will not generate either of the extreme values, so it has nothing to do with as.integer - acylam
但是某个点,runif()不应该返回52吗?但是你的观点关于使用as.integer()是正确的。我以为它会四舍五入,事实上它只是去掉小数部分。 - user2059737
如果您想从离散分布中进行抽样,则应使用sample。runif不适用于离散分布。 - kangaroo_cliff
@useR,使用runif生成任何值的概率都是0,不仅仅是端点值。OP尝试使用as.integer将连续分布转换为离散分布。 - kangaroo_cliff
我的观点不是说极端点有大于0的生成机会,而是这些极端点甚至没有被设计考虑在内。请参考@Ben的答案。 - acylam
好的。我的回答实际上是,除非runif恰好给出52,否则OP将看不到52(即51.999仍然不是52)。 这实际上就是他试图做的事情不起作用的原因。 即使他在运行大量模拟之后获得了52,这也不是他想要的。 - kangaroo_cliff

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