Random 如何有效地对连续负二项分布进行采样?

Random 如何有效地对连续负二项分布进行采样?,random,statistics,Random,Statistics,首先,在上下文中,我正在做一个游戏,当你做好事时,你会得到正积分,当你做坏事时,你会得到负积分,每个积分都对应于抛一个有偏见的硬币,如果你得到了正面的积分,那么事情就会发生(正面的积分是好的,负面的积分是坏的)否则什么也不会发生 交易是,我想处理多学分和分数学分的情况,我想让翻转使用学分,这样,如果发生了好/坏的事情,剩余的学分就会结转。一种简单的方法是只进行一系列试验,特别是对于分数学分的情况,我们可以将学分数乘以X,将发生某件事的可能性乘以1/X(分布具有相同的预期,但权重略有不同);不幸的

首先,在上下文中,我正在做一个游戏,当你做好事时,你会得到正积分,当你做坏事时,你会得到负积分,每个积分都对应于抛一个有偏见的硬币,如果你得到了正面的积分,那么事情就会发生(正面的积分是好的,负面的积分是坏的)否则什么也不会发生

交易是,我想处理多学分和分数学分的情况,我想让翻转使用学分,这样,如果发生了好/坏的事情,剩余的学分就会结转。一种简单的方法是只进行一系列试验,特别是对于分数学分的情况,我们可以将学分数乘以X,将发生某件事的可能性乘以1/X(分布具有相同的预期,但权重略有不同);不幸的是,这对用户可以获得多少学分以及学分数量中的小数位数设置了实际限制,因为这会导致无限量的工作

我想做的是利用这样一个事实,即我正在对连续负二项分布进行采样,这是获得头部所需的试验次数的分布,也就是说,如果f(X)是分布,那么f(X)给出了在我们遇到头部之前有X个尾部的概率,其中X不必是整数。如果我能对这个分布进行抽样,那么我能做的就是,如果X是尾数,那么我可以看到X是大于还是小于信用数;如果它大于,那么我们用光了所有的积分,但什么也没有发生,如果它小于或等于,那么会发生一些好事,我们从积分数中减去X。此外,由于分布是连续的,我可以很容易地处理分数学分


有人知道我有什么方法可以有效地对连续负二项分布(即从该分布生成随机数的函数)进行采样吗?

在StatExchange上回答这个问题可能更好,但在这里我将尝试一下

您是正确的,因为无法避免beta和/或gamma函数的依赖关系,所以直接计算此函数将在计算上非常昂贵。我所知道的唯一统计上有效的近似值是,如果所需的成功次数
s
很大,并且
p
既不是很小也不是很大,那么你可以用一个正态分布来近似它,其中有均值和方差的特殊值。你可以读更多,但我猜这个近似值一般不适用于你

负二项分布也可以近似为泊松分布的混合,但这并不能避免伽马函数依赖性

我所知道的唯一有效的负二项采样器使用优化的接受-拒绝技术。本PDF第10-11页描述了该方法背后的概念。本PDF第6页(内部第295页)包含使用相关技术对二项偏差进行采样的源代码。请注意,即使这些方法仍然需要随机均匀偏差以及
sqrt()
log()
、和
gammln()
调用。对于少量试验(可能少于100次?),如果仅仅用快速随机数生成器模拟试验比接受-拒绝技术都快,我一点也不会感到惊讶。一定要从快速PRNG开始;他们并非生来平等

编辑:

只要
p
不是非常大(太接近1.0),下面的伪代码可能非常有效地绘制随机离散负二项分布值。它将返回在达到您的第一个“期望”结果之前所需的试验次数(实际上,就分布而言,这是第一个“失败”):

//假设p和r是neg的参数。二项分布。
//r=失败次数(您将根据需要设置为1)
//p=成功的概率
double rnd=_rnd.nextDouble();//[0.0, 1.0)
int k=0;//表示在第一次失败之前发生的成功次数
双lastPmf=(1-p)^r;
双cdf=最后一个PMF;
而(cdf

使用递归关系可以避免在每一步单独重复阶乘。我认为使用它,再加上将分数精度限制在小数点后1或2位(因此只需分别乘以10或100)可能对你有用。你只画了一个随机数,其余的只是乘法——应该很快。

谢谢你的链接!我最初的计划是直接模拟东西,但后来我意识到,如果玩家得到1000学分,我允许小数点后两位,那么我需要进行100000次尝试,我会而不是给学分设置上限。仔细想想,如果我只限制小数位数并使用离散分布,可能会更好;你有如何取样的参考资料吗?@GregoryCrosswhite请看我的编辑——这个方法应该很快。哦,哇,这看起来就像我在我会试试的。
// assume p and r are the parameters to the neg. binomial dist.
// r = number of failures (you'll set to one for your purpose)
// p = probability of a "success"
double rnd = _rnd.nextDouble(); // [0.0, 1.0)
int k = 0;  // represents the # of successes that occur before 1st failure
double lastPmf = (1 - p)^r;
double cdf = lastPmf;
while (cdf < rnd)
{
    lastPmf *= (p * (k+r) / (k+1));
    cdf += lastPmf;
    k++;
}
return k;
// or return (k+1) to also count the trial on which the failure occurred