为什么我对随机数发生器的停车场测试实施产生不良结果?

时间:2012-11-12 01:48:27

标签: c# algorithm math statistics prng

我正在尝试为随机数生成器编写停车场测试的实现。以下是我从Intel math library documentationPage 4 of this paper以及列出的here概率密度的phi函数获取测试信息的来源。

我在C#中编写了一个测试实现。它使用100x100网格,其值最初设置为null。然后我使用随机数生成器为x和y生成随机整数。如果网格及其邻居的索引为空,则该索引设置为1.否则,没有任何反应,因为存在“崩溃”。

我使用C#System.Random生成器运行它。我不相信结果是正确的,因为我总是得到非常接近3079点停放,这比我应该得到的平均值短约500。它的p值也为2.21829146215425E-90。

我的代码如下。有没有人有这方面的经验,或者任何人都可以看到我在实施中可能做错的事情?任何帮助将不胜感激。

  private void RunParkingLotTest()
    {
        points = new int?[100,100];
        int parked = 0;

        for (int i = 0; i < 12000; i++)
        {
            int x = random.Next(100);
            int y = random.Next(100);

            if (IsSafeToPark(x, y))
            {
                points[x, y] = 1;
                parked++;
            }

        }
        Console.WriteLine("Parked: " + parked + "\nP value: " + PhiFunction((parked-3523)/21.9));
    }

    private bool IsSafeToPark(int x, int y)
    {
        return PointIsEmpty(x, y) 
            && LeftOfPointIsEmpty(x, y) 
            && RightOfPointIsEmpty(x, y) 
            && BelowPointIsEmpty(x, y) 
            && AbovePointIsEmpty(x, y);
    }

    private bool AbovePointIsEmpty(int x, int y)
    {
        if (y == 99)
        {
            return true;
        }
        else
            return points[x, y + 1] == null;
    }

    private bool BelowPointIsEmpty(int x, int y)
    {
        if (y == 0)
        {
            return true;
        }
        else
            return points[x, y - 1] == null;
    }

    private bool RightOfPointIsEmpty(int x, int y)
    {
        if (x == 99)
        {
            return true;
        }
        else
            return points[x + 1, y] == null;
    }

    private bool LeftOfPointIsEmpty(int x, int y)
    {
        if (x == 0)
        {
            return true;
        }
        else
            return points[x - 1, y] == null;
    }

    private bool PointIsEmpty(int x, int y)
    {
        return points[x, y] == null;
    }

    private double PhiFunction(double x)
    {
        //ϕ(x) = (2π)−½e−x2/2

        return ((1 / Math.Sqrt(2 * Math.PI)) * Math.Exp(-(Math.Pow(x, 2)) / 2));
    }

编辑 - 原始实现的问题是

  • 我正在绘制正方形而不是磁盘
  • 我只在整数值上绘制点。我应该使用小数值。
  • 由于上述两项原因,我需要更改我的距离检查

感谢Chris Sinclair和我的z帮助解决这个问题。最终代码发布在下面。

1 个答案:

答案 0 :(得分:4)

我要对此进行一次尝试,并且不可否认,我没有尝试任何此类测试,所以请原谅我,如果我离开的话。一般来说,.NET Random实现非常好,我从来没有遇到过它的问题,所以我最初不会怀疑,特别是因为你正在重用同一个实例而不是创建新的实例。

从parking.pdf和英特尔文档中读取,似乎他们正在使用光盘,并计算距其中心点的距离。你的实现是使用正方形(点之间的距离为1的阵列),因此忽略了对角线。

来自pdf:

  

如果使用磁盘,则粒子之间的距离为r =   p(x(i)-z)2 +(y(i)-z)2需要小于或等于1。   是否使用磁盘或方块是否重要?表明了   可以通过以下方式获得停放几何图形的重要性   将1.0边的正方形所占的面积与a的面积进行比较   直径为1.0的圆盘。磁盘与平方的面积比为π/ 4。   因此,可以预期可以放入更多磁盘   一个方框,而不是相同尝试次数的方块。

英特尔医生:

  

测试假定下一个随机点(x,y)成功“停放”,如果   它远远超过之前成功的“停放”点。该   点(x1,y1)和(x2,y2)之间的足够距离是   min(| x1 - x2 |,| y1 - y2 |)&gt; 1。

我猜测π/ 4磁盘与平方的比例以及可以与正方形匹配的磁盘数量之间的差异可能就是您看到不同数字的原因。 (虽然现在我没有看到3523和3070之间的直接关系和π/ 4.3523 *π/ 4 = 2767,这很接近,但我确定是否存在关系,它比简单的乘法稍微复杂一点。 )

不是一个好的答案,但我最好的猜测。

编辑:有趣的是,我使用1单位直径的光盘快速实现了并且获得了大约4000个停放的结果。所以也许这比我未经训练的自己能够掌握的更多(或者.NET Random没有通过测试?)无论如何,这是我的光盘实现:

List<Point> parkedCars = new List<Point>();
Random random = new Random();

void Main()
{
    int parked = 0;

    for (int i = 0; i < 12000; i++)
    {
        double x = random.NextDouble() * 100;
        double y = random.NextDouble() * 100;

        Point pointToPark = new Point(x, y);

        if (IsSafeToPark(pointToPark))
        {
            parkedCars.Add(pointToPark);
            parked++;
        }

    }
    Console.WriteLine("Parked: " + parked);
}

private bool IsSafeToPark(Point pointToPark)
{
    //make sure it's "inside" the box
    if (pointToPark.X < 0.5 || pointToPark.X > 99.5
        || pointToPark.Y < 0.5 || pointToPark.Y > 99.5)
        return false;

    if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
        return false;

    return true;
}

private double Distance(Point p1, Point p2)
{
    return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}

使用我可能过于简单的π/ 4比率应用产生约3142.更接近,但似乎非常不正确。

编辑:正如@mike z指出的那样,我直接使用距离的测试是不正确的。根据我忘记的测试参数,只检查X和Y距离是否大于1.将我的Distance检查更改为:

Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y))

在3450附近产生更接近的结果,这非常接近。如果我在“框中”检查中取出“//确定它是”,平均超过10次尝试获得3531!

所以我的最终“工作”代码是:

public struct Point
{
    public double X,Y;

    public Point(double x, double y)
    {
        this.X = x;
        this.Y = y;
    }
}

List<Point> parkedCars = new List<Point>();
Random random = new Random();

void Main()
{
    int parked = 0;

    for (int i = 0; i < 12000; i++)
    {
        double x = random.NextDouble() * 100;
        double y = random.NextDouble() * 100;

        Point pointToPark = new Point(x, y);

        if (IsSafeToPark(pointToPark))
        {
            parkedCars.Add(pointToPark);
            parked++;
        }

    }

    Console.WriteLine("Parked: " + parked);
}

private bool IsSafeToPark(Point pointToPark)
{
    if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
        return false;

    return true;
}

private double Distance(Point p1, Point p2)
{
    return Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));
}
编辑:我测试了两次100次,并将结果平均分别为3521.29和3526.74。不确定这是否意味着仍然更多的东西,但这或许只是表明.NET和Fortran之间的舍入或浮点精度差异。