C# 如何有效地进行大量随机试验?
假设一个事件有成功的概率<代码>(0C# 如何有效地进行大量随机试验?,c#,algorithm,formula,probability,C#,Algorithm,Formula,Probability,假设一个事件有成功的概率(0
我必须进行
N
测试,看看是否会发生这种情况,我想要成功的总数:
我可以去
int countSuccesses = 0;
while(N-- > 0)
{
if(Random.NextDouble()<P) countSuccesses++; // NextDouble is from 0.0 to 1.0
}
它是O(1)
更新我会尽量和你一起去
MathNet.Numerics.Distributions.二项式样本(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)
根据你对问题的措辞,这是不可能做到的
你本质上是在问如何确保一次投币(即一次随机结果)正好是50%正面和50%反面,这是不可能的
即使你使用两个随机数,你期望一个正面和一个反面;这种测试在所有情况下都会失败50%(因为你可能会得到两个正面或两个反面)
概率是建立在时间的基础上的。这明确指出,小样本不能准确反映预期结果
LLN很重要,因为它保证了一些随机事件平均值的长期稳定结果。例如,虽然一家赌场可能在轮盘赌的一次旋转中赔钱,但在大量旋转中,其收益将趋向于一个可预测的百分比。玩家的任何连胜最终都会被游戏的参数所克服。重要的是要记住,法律只适用于(如名称所示)当大量的观察被考虑没有一个原则,即少量观察值将与预期值一致,或者一个值的条纹将立即被其他值“平衡”(参见赌徒谬论)
当我问这是一个评论;你回答说:
@更平淡的是,我正在进行N次实际抽签,但只有一个随机数
但这毫无意义。如果你只使用一个随机值,并继续使用相同的值,那么很明显,每次抽签都会得到完全相同的结果(相同的数字)
我能用一种并非不可能的方式来解释你的问题的最接近的解释是,你错误地将一个单一的随机数称为一个单一的随机数
随机种子(或种子状态,或仅种子)是用于初始化伪随机数生成器的数字(或向量)
对于要在伪随机数生成器中使用的种子,它不需要是随机的。由于数字生成算法的性质,只要忽略原始种子,算法生成的其余值将以伪随机方式遵循概率分布
然而,你明确提到的期望似乎推翻了这一假设。您希望执行以下操作:
GetSuccesses( n, P, Random.NextDouble())
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;
}
你还期望得到一个O(1)
运算,它违背了大数定律
如果你说的是一个随机种子;那么你的期望是不正确的
- 如果进行N次绘制,则操作仍然具有
O(N)
复杂性。无论你是否在每次抽签后随机化种子都是无关紧要的,它总是O(N)
getsuccessfuls(n,P,Random.NextDouble())
将为您提供一个抽签,而不是一个种子。无论使用何种术语,您对代码的期望与对多个绘图使用同一种子无关
正如问题目前的措辞;你想要的是不可能的。几位评论者反复发表评论要求澄清,但还没有得出一个更清晰的画面
作为旁注,我觉得很奇怪的是,除了直接被问到你是否在谈论一个种子而不是一个数字(现在两次)时,你对每一条评论都做出了回答。我不打算在这里写公式,因为它已经在维基上了,我真的不知道这类东西的好格式
每个结果的概率可通过Bernulli公式确定
你们需要做的是计算二项式系数,然后概率计算变得非常简单——用二项式系数乘以p和q的适当幂。填写数组P[0..n],该数组包含每个结果的概率-恰好i个成功数
设置后,从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];
sum=0;
对于(int i=0;i R)
返回i;
其他的
总和+=P[i];
这里eps是一个小的浮点值,用来克服浮点舍入问题,R是保存的随机值,p是我前面提到的一个概率数组
不幸的是,这种方法不适用于大N(20或100+):
- 舍入误差会给你带来很大的影响
- 随机数生成器可能不具有足够的确定性,无法用适当的概率分布覆盖所有可能的结果
对于小N,您可以使用二项分布的CDF或PMF,并简单地将随机输入与0,1,2..N成功的概率进行比较
比如:
GetSuccesses( n, P, Random.NextDouble())
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;
}
static void Main(字符串[]args)
{
var试验=10;
var三概率=0.25;
for(double p=0;p将我指向
MathNet.Numer
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);
}