C# 如何有效地进行大量随机试验?

C# 如何有效地进行大量随机试验?,c#,algorithm,formula,probability,C#,Algorithm,Formula,Probability,假设一个事件有成功的概率(0

假设一个事件有成功的概率<代码>(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);
    }