Generate a random point within a circle (uniformly)

前端 未结 21 2352
逝去的感伤
逝去的感伤 2020-11-22 15:45

I need to generate a uniformly random point within a circle of radius R.

I realize that by just picking a uniformly random angle in the interval [0 ... 2π),

相关标签:
21条回答
  • 2020-11-22 16:11

    I am still not sure about the exact '(2/R2)×r' but what is apparent is the number of points required to be distributed in given unit 'dr' i.e. increase in r will be proportional to r2 and not r.

    check this way...number of points at some angle theta and between r (0.1r to 0.2r) i.e. fraction of the r and number of points between r (0.6r to 0.7r) would be equal if you use standard generation, since the difference is only 0.1r between two intervals. but since area covered between points (0.6r to 0.7r) will be much larger than area covered between 0.1r to 0.2r, the equal number of points will be sparsely spaced in larger area, this I assume you already know, So the function to generate the random points must not be linear but quadratic, (since number of points required to be distributed in given unit 'dr' i.e. increase in r will be proportional to r2 and not r), so in this case it will be inverse of quadratic, since the delta we have (0.1r) in both intervals must be square of some function so it can act as seed value for linear generation of points (since afterwords, this seed is used linearly in sin and cos function), so we know, dr must be quadratic value and to make this seed quadratic, we need to originate this values from square root of r not r itself, I hope this makes it little more clear.

    0 讨论(0)
  • 2020-11-22 16:12

    How to generate a random point within a circle of radius R:

    r = R * sqrt(random())
    theta = random() * 2 * PI
    

    (Assuming random() gives a value between 0 and 1 uniformly)

    If you want to convert this to Cartesian coordinates, you can do

    x = centerX + r * cos(theta)
    y = centerY + r * sin(theta)
    


    Why sqrt(random())?

    Let's look at the math that leads up to sqrt(random()). Assume for simplicity that we're working with the unit circle, i.e. R = 1.

    The average distance between points should be the same regardless of how far from the center we look. This means for example, that looking on the perimeter of a circle with circumference 2 we should find twice as many points as the number of points on the perimeter of a circle with circumference 1.


                    

    Since the circumference of a circle (2πr) grows linearly with r, it follows that the number of random points should grow linearly with r. In other words, the desired probability density function (PDF) grows linearly. Since a PDF should have an area equal to 1 and the maximum radius is 1, we have


                    

    So we know how the desired density of our random values should look like. Now: How do we generate such a random value when all we have is a uniform random value between 0 and 1?

    We use a trick called inverse transform sampling

    1. From the PDF, create the cumulative distribution function (CDF)
    2. Mirror this along y = x
    3. Apply the resulting function to a uniform value between 0 and 1.

    Sounds complicated? Let me insert a blockquote with a little side track that conveys the intuition:

    Suppose we want to generate a random point with the following distribution:

                    

    That is

    • 1/5 of the points uniformly between 1 and 2, and
    • 4/5 of the points uniformly between 2 and 3.

    The CDF is, as the name suggests, the cumulative version of the PDF. Intuitively: While PDF(x) describes the number of random values at x, CDF(x) describes the number of random values less than x.

    In this case the CDF would look like:

                    

    To see how this is useful, imagine that we shoot bullets from left to right at uniformly distributed heights. As the bullets hit the line, they drop down to the ground:

                    

    See how the density of the bullets on the ground correspond to our desired distribution! We're almost there!

    The problem is that for this function, the y axis is the output and the x axis is the input. We can only "shoot bullets from the ground straight up"! We need the inverse function!

    This is why we mirror the whole thing; x becomes y and y becomes x:

                    

    We call this CDF-1. To get values according to the desired distribution, we use CDF-1(random()).

    …so, back to generating random radius values where our PDF equals 2x.

    Step 1: Create the CDF:

    Since we're working with reals, the CDF is expressed as the integral of the PDF.

    CDF(x) = ∫ 2x = x2

    Step 2: Mirror the CDF along y = x:

    Mathematically this boils down to swapping x and y and solving for y:

    CDF:     y = x2
    Swap:   x = y2
    Solve:   y = √x
    CDF-1:  y = √x

    Step 3: Apply the resulting function to a uniform value between 0 and 1

    CDF-1(random()) = √random()

    Which is what we set out to derive :-)

    0 讨论(0)
  • 2020-11-22 16:12

    I used once this method: This may be totally unoptimized (ie it uses an array of point so its unusable for big circles) but gives random distribution enough. You could skip the creation of the matrix and draw directly if you wish to. The method is to randomize all points in a rectangle that fall inside the circle.

    bool[,] getMatrix(System.Drawing.Rectangle r) {
        bool[,] matrix = new bool[r.Width, r.Height];
        return matrix;
    }
    
    void fillMatrix(ref bool[,] matrix, Vector center) {
        double radius = center.X;
        Random r = new Random();
        for (int y = 0; y < matrix.GetLength(0); y++) {
            for (int x = 0; x < matrix.GetLength(1); x++)
            {
                double distance = (center - new Vector(x, y)).Length;
                if (distance < radius) {
                    matrix[x, y] = r.NextDouble() > 0.5;
                }
            }
        }
    
    }
    
    private void drawMatrix(Vector centerPoint, double radius, bool[,] matrix) {
        var g = this.CreateGraphics();
    
        Bitmap pixel = new Bitmap(1,1);
        pixel.SetPixel(0, 0, Color.Black);
    
        for (int y = 0; y < matrix.GetLength(0); y++)
        {
            for (int x = 0; x < matrix.GetLength(1); x++)
            {
                if (matrix[x, y]) {
                    g.DrawImage(pixel, new PointF((float)(centerPoint.X - radius + x), (float)(centerPoint.Y - radius + y)));
                }
            }
        }
    
        g.Dispose();
    }
    
    private void button1_Click(object sender, EventArgs e)
    {
        System.Drawing.Rectangle r = new System.Drawing.Rectangle(100,100,200,200);
        double radius = r.Width / 2;
        Vector center = new Vector(r.Left + radius, r.Top + radius);
        Vector normalizedCenter = new Vector(radius, radius);
        bool[,] matrix = getMatrix(r);
        fillMatrix(ref matrix, normalizedCenter);
        drawMatrix(center, radius, matrix);
    }
    

    enter image description here

    0 讨论(0)
  • 2020-11-22 16:14

    Note the point density in proportional to inverse square of the radius, hence instead of picking r from [0, r_max], pick from [0, r_max^2], then compute your coordinates as:

    x = sqrt(r) * cos(angle)
    y = sqrt(r) * sin(angle)
    

    This will give you uniform point distribution on a disk.

    http://mathworld.wolfram.com/DiskPointPicking.html

    0 讨论(0)
  • 2020-11-22 16:14

    Let ρ (radius) and φ (azimuth) be two random variables corresponding to polar coordinates of an arbitrary point inside the circle. If the points are uniformly distributed then what is the disribution function of ρ and φ?

    For any r: 0 < r < R the probability of radius coordinate ρ to be less then r is

    P[ρ < r] = P[point is within a circle of radius r] = S1 / S0 =(r/R)2

    Where S1 and S0 are the areas of circle of radius r and R respectively. So the CDF can be given as:

              0          if r<=0
      CDF =   (r/R)**2   if 0 < r <= R
              1          if r > R
    

    And PDF:

    PDF = d/dr(CDF) = 2 * (r/R**2) (0 < r <= R).
    

    Note that for R=1 random variable sqrt(X) where X is uniform on [0, 1) has this exact CDF (because P[sqrt(X) < y] = P[x < y**2] = y**2 for 0 < y <= 1).

    The distribution of φ is obviously uniform from 0 to 2*π. Now you can create random polar coordinates and convert them to Cartesian using trigonometric equations:

    x = ρ * cos(φ)
    y = ρ * sin(φ)
    

    Can't resist to post python code for R=1.

    from matplotlib import pyplot as plt
    import numpy as np
    
    rho = np.sqrt(np.random.uniform(0, 1, 5000))
    phi = np.random.uniform(0, 2*np.pi, 5000)
    
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    
    plt.scatter(x, y, s = 4)
    

    You will get

    0 讨论(0)
  • 2020-11-22 16:15

    It really depends on what you mean by 'uniformly random'. This is a subtle point and you can read more about it on the wiki page here: http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29, where the same problem, giving different interpretations to 'uniformly random' gives different answers!

    Depending on how you choose the points, the distribution could vary, even though they are uniformly random in some sense.

    It seems like the blog entry is trying to make it uniformly random in the following sense: If you take a sub-circle of the circle, with the same center, then the probability that the point falls in that region is proportional to the area of the region. That, I believe, is attempting to follow the now standard interpretation of 'uniformly random' for 2D regions with areas defined on them: probability of a point falling in any region (with area well defined) is proportional to the area of that region.

    0 讨论(0)
提交回复
热议问题