假设某个事件的概率 P
成功。 (0 < P < 1 )
我必须进行N
测试,看看是否会发生这种情况,我想要成功的总数:
我可以去
int countSuccesses = 0;
while(N-- > 0)
{
if(Random.NextDouble()<P) countSuccesses++; // NextDouble is from 0.0 to 1.0
}
但有没有更有效的方法呢?我想要一个公式,所以我可以使用一个draw 随机数来确定成功的总数。 (编辑仅使用一次抽奖的想法是低于O(n))
我希望能够调用方法
GetSuccesses( n, P)
它是O(1)
更新
我会试着去
MathNet.Numerics.Distributions.Binomial.Sample(P, n)
即使它可能只使用了一个随机数,但我猜它会比O(n)
更快,即使它不是O(1)
。我会对此进行基准测试。非常感谢David和Rici。
更新
上面的二项式样本是O(n)所以它对我没有帮助。但是由于弗雷德的评论,我刚刚切换到了
MathNet.Numerics.Distributions.Normal.Sample(mean, stddev)
其中
mean = n * P
stddev = Math.Sqrt(n * P * (1 - P));
现在是O(1)
!
答案 0 :(得分:2)
对于小N,每个@rici你可以使用二项分布的CDF或PMF,并简单地将随机输入与0,1,2..N成功的概率进行比较。
类似的东西:
static void Main(string[] args)
{
var trials = 10;
var trialProbability = 0.25;
for (double p = 0; p <= 1; p += 0.01)
{
var i = GetSuccesses(trials, trialProbability, p);
Console.WriteLine($"{i} Successes out of {trials} with P={trialProbability} at {p}");
}
Console.ReadKey();
}
static int GetSuccesses(int N, double P, double rand)
{
for (int i = 0; i <= N; i++)
{
var p_of_i_successes = MathNet.Numerics.Distributions.Binomial.PMF(P, N, i);
if (p_of_i_successes >= rand)
return i;
rand -= p_of_i_successes;
}
return N;
}
答案 1 :(得分:1)
我不打算在这里写公式,因为它已经在wiki中了,而且我不太清楚这些事情的格式。
每个结果的概率可以通过Bernulli公式确定 https://en.wikipedia.org/wiki/Bernoulli_trial
你需要做的是计算二项式系数,然后概率计算变得非常容易 - 将二项式系数乘以p和q的适当幂。填写数组P [0..n],其中包含每个结果的概率 - 完全成功的数量。
设置后从0到n并计算概率的滚动总和。 检查下限/上限与随机值,一旦在当前时间间隔内,返回结果。
因此,决定部分将是这样的:
sum=0;
for (int i = 0; i <= n; i++)
if (sum-eps < R && sum+P[i]+eps > R)
return i;
else
sum+=P[i];
这里eps是小浮点值以克服浮点舍入问题,R是保存的随机值,P是我之前提到的概率数组。
不幸的是,这种方法对于大N(20或100 +)不实用:
你会对舍入错误产生很大的影响
随机数生成器可能不具有足够的确定性,无法通过适当的概率分布来涵盖所有可能的结果
答案 2 :(得分:0)
根据你如何表达问题,这是不可能做到的。
你基本上是在询问如何确保单个硬币翻转(即一个随机化结果)正好是50%的头部和50%的尾部,这是不可能的。
即使你要使用两个随机数,你期望一个头和一个尾巴;在所有情况下,这个测试都会失败50%(因为你可能得到两个头或两个尾巴)。
Probablility建立在the law of large numbers之上。这明确指出,不能期望小样本准确反映预期结果。
LLN很重要,因为它可以保证一些随机事件平均值的长期稳定结果。例如,虽然赌场可能会在轮盘赌的单轮旋转中亏损,但其盈利将在大量旋转中趋于可预测的百分比。任何玩家的连胜最终都会被游戏的参数所克服。重要的是要记住,当考虑大量观察时,法律仅适用(如名称所示)。 没有原则,少数观察会与预期值重合,或者一个值的连续性会立即被其他值“平衡”(参见赌徒的谬误)。
当我问这个评论时;你回答说:
@Flater不,我正在进行N次实际抽签,但只有一个随机数。
但这没有意义。如果你只使用一个随机值,并继续使用相同的值,那么每次抽取显然会给你完全相同的结果(相同的数字)。
我能以一种并非不可能的方式解释你的问题,那就是你错误地将单个random seed称为单个随机数。
随机种子(或种子状态,或仅种子)是用于初始化伪随机数生成器的数字(或向量)。
对于要在伪随机数生成器中使用的种子,它不需要是随机的。由于数字生成算法的性质,只要忽略原始种子,算法生成的其余值将以伪随机方式遵循概率分布。
但是,您明确提到的期望似乎反驳了这一假设。你想做类似的事情:
GetSuccesses( n, P, Random.NextDouble())
并且你也希望得到一个O(1)
操作,这个操作面对大数定律。
如果你真的在谈论有一个随机种子;那么你的期望是不正确的。
O(N)
。无论你是否在每次抽奖后随机化种子都是无关紧要的,它总是O(N)
。GetSuccesses( n, P, Random.NextDouble())
将为您提供一次抽奖,而非一粒种子。无论使用何种术语,您对代码的期望与使用相同种子进行多次绘制无关。目前的问题是措辞;你想要的是不可能的。一些评论者的反复意见澄清尚未产生更清晰的图景。
作为旁注,我发现你回答每个评论除了直接询问你是在谈论种子而不是数字(现在两次)时,我觉得很奇怪。
答案 3 :(得分:0)
@David and @rici向我指出了
MathNet.Numerics.Distributions.Binomial.Sample(P, n)
一个基准测试告诉我它也是O(n)
并与我原来的
int countSuccesses = 0;
while(N-- > 0)
{
if(Random.NextDouble()<P) countSuccesses++; // NextDouble is from 0.0 to 1.0
}
但感谢Fred做的评论:
您可以将该随机数转换为具有均值N * P的高斯样本,其与您的初始函数具有相同的分布
我刚转到了
MathNet.Numerics.Distributions.Normal.Sample(mean, stddev)
其中
mean = n * P
stddev = Math.Sqrt(n * P * (1 - P));
现在是O(1)
!
我想要的功能是:
private int GetSuccesses(double p, int n)
{
double mean = n * p;
double stddev = Math.Sqrt(n * p * (1 - p));
double hits = MathNet.Numerics.Distributions.Normal.Sample(Random, mean, stddev);
return (int)Math.Round(hits, 0);
}
保罗指出, 这是一个近似值,但我很乐意接受。