代码之家  ›  专栏  ›  技术社区  ›  Daniel

使用系统输出代码。随着迭代次数的增加,随机性不接近理论极限

  •  1
  • Daniel  · 技术社区  · 7 年前

    我不擅长统计,所以我试图用C语言解决一个简单的问题。问题是:“一支给定的球队有65%的机会在与另一支球队的比赛中获胜。他们赢得五局五胜制的概率是多少?”

    我想看看概率和比赛次数之间的关系。Bo3与Bo5相比如何,等等?

    我通过创建 Set Game 对象和运行迭代。win决策使用以下代码完成:

    Won = rnd.Next(1, 100) <= winChance;
    

    rnd 正如您所料,它是静态的 System.Random 对象

    这是我的 设置 目标代码:

    public class Set
    {
        public int NumberOfGames { get; private set; }
        public List<Game> Games { get; private set; }
        public Set(int numberOfGames, int winChancePct)
        {
            NumberOfGames = numberOfGames;
            GamesNeededToWin = Convert.ToInt32(Math.Ceiling(NumberOfGames / 2m));
            Games = Enumerable.Range(1, numberOfGames)
                              .Select(i => new Game(winChancePct))
                              .ToList();
        }
    
        public int GamesNeededToWin { get; private set; }
        public bool WonSet => Games.Count(g => g.Won) >= GamesNeededToWin;
    }
    

    我的问题是,我得到的结果并不是我应该得到的结果。有人在统计方面做得不太好,他帮我算了算,我的代码似乎总是高估了赢得这一集的机会,迭代次数并没有提高准确性。

    我得到的结果(每场比赛赢多少局)如下。第一列是每套游戏,第二列是统计获胜率(我的结果应该接近),其余列是基于迭代次数的结果。正如你所见,更多的迭代似乎并不能使数字更准确。

    每盘比赛|预期盘赢率| 10K | 100K | 1M | 10M

    1 65.0% 66.0% 65.6% 65.7% 65.7%

    5 76.5% 78.6% 77.4% 77.5% 77.5%

    7 80.0% 80.7% 81.2% 81.0% 81.1%

    9 82.8% 84.1% 83.9% 83.9% 83.9%

    整个项目发布在github上 here 如果你想看的话。

    如果能深入了解为什么这不能产生准确的结果,我们将不胜感激。

    2 回复  |  直到 7 年前
        1
  •  6
  •   Eric Lippert    7 年前

    达伦·西松的回答是正确的;您的计算大约减少了1%,因此您的所有结果也是如此。

    我的建议是,通过将所需的语义封装到一个对象中来解决问题,然后可以独立测试:

    interface IDistribution<T>
    {
      T Sample();
    }
    static class Extensions 
    {
      public static IEnumerable<T> Samples(this IDistribution<T> d)
      {
        while (true) yield return d.Sample();
      }
    }
    class Bernoulli : IDistribution<bool>
    {
      // Note that we could also make it IDistribution<int> and return
      // 0 and 1 instead of false and true; that would be the more 
      // "classic" approach to a Bernoulli distribution.  Your choice.
      private double d;
      private Random random = new Random();
      private Bernoulli(double d) { this.d = d; }
      public static Make(double d) => new Bernoulli(d);
      public bool Sample() => random.NextDouble() < d;
    }
    

    现在你有一个有偏见的抛硬币器,你可以独立测试。现在,您可以编写如下代码:

    int flips = 1000;
    int heads = Bernoulli
      .Make(0.65)
      .Samples()
      .Take(flips)
      .Where(x => x)
      .Count();
    

    以65%的概率掷1000个硬币。

    注意,我们在这里所做的是构造一个 概率分布单子 然后使用LINQ工具表示条件概率。这是一种强大的技术;你的应用程序几乎没有触及我们可以用它做什么的表面。

    练习:构造扩展方法 Where , Select SelectMany 哪一个不需要 IEnumerable<T> 而是 IDistribution<T> ; 你能用分布类型本身来表达分布的语义吗,而不是从分布单子转换为序列单子?你能为zip连接做同样的事情吗?

    练习:构建的其他实现 IDistribution<T> . 你能构造,比方说,双倍的柯西分布吗?那么正态分布呢?在n个边的公平骰子上掷骰子如何?现在,你能把这些放在一起吗?分布是什么:掷硬币;如果是正面,掷四个骰子并将其相加,否则掷两个骰子并丢弃所有的双倍骰子,然后将结果相乘。

        2
  •  1
  •   Darren Sisson    7 年前

    快看,随机函数的上限是独占的,因此需要设置为101