Facebook Pixel

478. Generate Random Point in a Circle

MediumGeometryMathRejection SamplingRandomized
Leetcode Link

Problem Description

You need to implement a class that generates random points uniformly distributed inside a circle. The circle is defined by its radius and center position coordinates.

The Solution class has two methods:

  • Constructor __init__(radius, x_center, y_center): Initializes the circle with:

    • radius: the radius of the circle
    • x_center, y_center: the coordinates of the circle's center
  • Random Point Generator randPoint(): Returns a randomly generated point [x, y] that lies inside or on the boundary of the circle

The key challenge is ensuring the points are uniformly distributed within the circle. This means every point inside the circle should have an equal probability of being selected.

The solution uses polar coordinates to generate uniform random points:

  1. Random radius: Uses length = math.sqrt(random.uniform(0, radius²)) to ensure uniform distribution. The square root is crucial - without it, points would cluster near the center.
  2. Random angle: Uses degree = random.uniform(0, 1) * 2π to get a random angle from 0 to 2π radians.
  3. Convert to Cartesian coordinates:
    • x = x_center + length * cos(degree)
    • y = y_center + length * sin(degree)

The mathematical insight: In polar coordinates, the area element is proportional to r * dr * dθ. To achieve uniform distribution in Cartesian space, we need to sample uniformly (not r directly), then take the square root. This compensates for the fact that circles at larger radii have more area than those at smaller radii.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The first instinct might be to generate random polar coordinates directly: pick a random radius r between 0 and radius, and a random angle θ between 0 and 2π. However, this approach would create a non-uniform distribution with points clustering near the center.

Why does this happen? Imagine dividing the circle into concentric rings. A ring at radius r has circumference 2πr, which means it has more area than rings closer to the center. If we pick radius values uniformly, we're giving equal probability to each radius value, but larger radii represent more area. This causes points to cluster near the center.

To visualize this: if we divide the circle into two regions - inner circle (radius 0 to r/2) and outer ring (radius r/2 to r), the outer ring actually contains 75% of the total area, while the inner circle only has 25%. Yet uniform radius selection would place points in each region with 50% probability.

The key insight is that the area of a circle grows with the square of the radius (A = πr²). Therefore, to achieve uniform distribution across the area, we need the probability of selecting a radius to be proportional to the radius itself (since more area exists at larger radii).

Mathematically, if we want uniform distribution over area, the cumulative distribution function (CDF) for radius should be proportional to r²/R² (where R is the circle's radius). To generate a random variable with this distribution, we:

  1. Generate a uniform random value u between 0 and 1
  2. Set u = r²/R²
  3. Solve for r: r = R * sqrt(u)

This is why the solution uses length = math.sqrt(random.uniform(0, radius²)) - it's equivalent to generating a uniform value between 0 and 1, multiplying by radius², then taking the square root.

Solution Approach

The implementation consists of a class with initialization and random point generation methods. Here's how each component works:

1. Class Initialization (__init__ method):

def __init__(self, radius: float, x_center: float, y_center: float):
    self.radius = radius
    self.x_center = x_center
    self.y_center = y_center

Store the circle's parameters as instance variables for use in point generation.

2. Random Point Generation (randPoint method):

The method generates uniformly distributed points using polar coordinates with proper transformation:

def randPoint(self) -> List[float]:
    length = math.sqrt(random.uniform(0, self.radius**2))
    degree = random.uniform(0, 1) * 2 * math.pi
    x = self.x_center + length * math.cos(degree)
    y = self.y_center + length * math.sin(degree)
    return [x, y]

Step-by-step breakdown:

  1. Generate random radius with proper distribution:

    • random.uniform(0, self.radius**2) generates a uniform random value between 0 and radius²
    • math.sqrt() transforms this to ensure uniform area distribution
    • This gives us length, the distance from center to our random point
  2. Generate random angle:

    • random.uniform(0, 1) generates a value between 0 and 1
    • Multiply by 2 * math.pi to get an angle in radians from 0 to 2π
    • This gives us degree, the angular position of our point
  3. Convert polar to Cartesian coordinates:

    • x = self.x_center + length * math.cos(degree) calculates the x-coordinate
    • y = self.y_center + length * math.sin(degree) calculates the y-coordinate
    • The center coordinates (x_center, y_center) are added to translate the point from origin to the actual circle center
  4. Return the result:

    • Return the point as a list [x, y]

Key algorithmic patterns used:

  • Inverse Transform Sampling: Using the inverse CDF to generate random variables with a specific distribution
  • Coordinate Transformation: Converting between polar and Cartesian coordinate systems
  • Translation: Shifting points from origin-centered circle to the actual center position

The time complexity is O(1) for each randPoint() call, and space complexity is O(1) as we only store the circle parameters.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through generating random points in a circle with radius 2.0 centered at (1.0, 3.0).

Step 1: Initialize the circle

solution = Solution(radius=2.0, x_center=1.0, y_center=3.0)

Step 2: Generate a random point

Let's trace through one call to randPoint():

  1. Generate random radius with uniform distribution:

    • Suppose random.uniform(0, 4.0) returns 3.0 (since radius² = 4.0)
    • Calculate length = sqrt(3.0) ≈ 1.732
    • This point will be ~1.732 units from the center
  2. Generate random angle:

    • Suppose random.uniform(0, 1) returns 0.25
    • Calculate degree = 0.25 * 2π ≈ 1.571 radians (90 degrees)
    • This point will be directly above the center
  3. Convert to Cartesian coordinates:

    • x = 1.0 + 1.732 * cos(1.571) ≈ 1.0 + 1.732 * 0 = 1.0
    • y = 3.0 + 1.732 * sin(1.571) ≈ 3.0 + 1.732 * 1 = 4.732
    • Final point: [1.0, 4.732]

Step 3: Verify the distribution

To understand why we use sqrt(random.uniform(0, radius²)) instead of just random.uniform(0, radius), consider dividing our circle into two regions:

  • Inner circle: radius 0 to 1.0 (area = π)
  • Outer ring: radius 1.0 to 2.0 (area = 4π - π = 3π)

The outer ring has 3 times the area of the inner circle, so it should receive 75% of the points.

With our approach:

  • Probability of point in inner circle: P(length ≤ 1) = P(sqrt(u) ≤ 1) = P(u ≤ 1) = 1/4 = 25%
  • Probability of point in outer ring: P(length > 1) = 75%

This matches the area distribution perfectly! If we had used random.uniform(0, radius) directly, both regions would get 50% of points, causing clustering near the center.

Multiple sample points: Calling randPoint() multiple times might generate:

  • [2.414, 3.707] - point in the upper-right quadrant
  • [0.123, 2.156] - point in the lower-left quadrant
  • [1.000, 3.000] - rare but possible point exactly at center
  • [-0.876, 3.543] - point in the upper-left quadrant

Each point satisfies: (x - 1.0)² + (y - 3.0)² ≤ 4.0

Solution Implementation

1import math
2import random
3from typing import List
4
5class Solution:
6    def __init__(self, radius: float, x_center: float, y_center: float):
7        """
8        Initialize the circle with given radius and center coordinates.
9      
10        Args:
11            radius: The radius of the circle
12            x_center: The x-coordinate of the circle's center
13            y_center: The y-coordinate of the circle's center
14        """
15        self.radius = radius
16        self.x_center = x_center
17        self.y_center = y_center
18
19    def randPoint(self) -> List[float]:
20        """
21        Generate a random point uniformly distributed within the circle.
22      
23        Returns:
24            A list containing [x, y] coordinates of the random point
25        """
26        # Generate random radius using square root for uniform distribution
27        # sqrt is used to ensure uniform distribution across the circle area
28        # Without sqrt, points would be more concentrated near the center
29        random_radius = math.sqrt(random.uniform(0, self.radius ** 2))
30      
31        # Generate random angle in radians (0 to 2π)
32        random_angle = random.uniform(0, 1) * 2 * math.pi
33      
34        # Convert polar coordinates to Cartesian coordinates
35        # x = center_x + r * cos(θ)
36        x = self.x_center + random_radius * math.cos(random_angle)
37        # y = center_y + r * sin(θ)
38        y = self.y_center + random_radius * math.sin(random_angle)
39      
40        return [x, y]
41
1import java.util.Random;
2
3class Solution {
4    private double radius;
5    private double xCenter;
6    private double yCenter;
7    private Random random;
8  
9    /**
10     * Initialize the circle with given radius and center coordinates.
11     * 
12     * @param radius The radius of the circle
13     * @param x_center The x-coordinate of the circle's center
14     * @param y_center The y-coordinate of the circle's center
15     */
16    public Solution(double radius, double x_center, double y_center) {
17        this.radius = radius;
18        this.xCenter = x_center;
19        this.yCenter = y_center;
20        this.random = new Random();
21    }
22  
23    /**
24     * Generate a random point uniformly distributed within the circle.
25     * 
26     * @return An array containing [x, y] coordinates of the random point
27     */
28    public double[] randPoint() {
29        // Generate random radius using square root for uniform distribution
30        // sqrt is used to ensure uniform distribution across the circle area
31        // Without sqrt, points would be more concentrated near the center
32        double randomRadius = Math.sqrt(random.nextDouble() * radius * radius);
33      
34        // Generate random angle in radians (0 to 2π)
35        double randomAngle = random.nextDouble() * 2 * Math.PI;
36      
37        // Convert polar coordinates to Cartesian coordinates
38        // x = center_x + r * cos(θ)
39        double x = xCenter + randomRadius * Math.cos(randomAngle);
40        // y = center_y + r * sin(θ)
41        double y = yCenter + randomRadius * Math.sin(randomAngle);
42      
43        return new double[]{x, y};
44    }
45}
46
1#include <vector>
2#include <cmath>
3#include <random>
4
5class Solution {
6private:
7    double radius;
8    double x_center;
9    double y_center;
10    std::mt19937 gen;  // Random number generator
11    std::uniform_real_distribution<double> dist;  // Uniform distribution [0, 1)
12  
13public:
14    /**
15     * Initialize the circle with given radius and center coordinates.
16     * 
17     * @param radius The radius of the circle
18     * @param x_center The x-coordinate of the circle's center
19     * @param y_center The y-coordinate of the circle's center
20     */
21    Solution(double radius, double x_center, double y_center) {
22        this->radius = radius;
23        this->x_center = x_center;
24        this->y_center = y_center;
25      
26        // Initialize random number generator with random device seed
27        std::random_device rd;
28        gen = std::mt19937(rd());
29        dist = std::uniform_real_distribution<double>(0.0, 1.0);
30    }
31  
32    /**
33     * Generate a random point uniformly distributed within the circle.
34     * 
35     * @return A vector containing [x, y] coordinates of the random point
36     */
37    std::vector<double> randPoint() {
38        // Generate random radius using square root for uniform distribution
39        // sqrt is used to ensure uniform distribution across the circle area
40        // Without sqrt, points would be more concentrated near the center
41        double randomRadius = std::sqrt(dist(gen) * radius * radius);
42      
43        // Generate random angle in radians (0 to 2π)
44        double randomAngle = dist(gen) * 2 * M_PI;
45      
46        // Convert polar coordinates to Cartesian coordinates
47        // x = center_x + r * cos(θ)
48        double x = x_center + randomRadius * std::cos(randomAngle);
49        // y = center_y + r * sin(θ)
50        double y = y_center + randomRadius * std::sin(randomAngle);
51      
52        return {x, y};
53    }
54};
55
1let radius: number;
2let xCenter: number;
3let yCenter: number;
4
5/**
6 * Initialize the circle with given radius and center coordinates.
7 * 
8 * @param _radius - The radius of the circle
9 * @param _x_center - The x-coordinate of the circle's center
10 * @param _y_center - The y-coordinate of the circle's center
11 */
12function Solution(_radius: number, _x_center: number, _y_center: number): void {
13    radius = _radius;
14    xCenter = _x_center;
15    yCenter = _y_center;
16}
17
18/**
19 * Generate a random point uniformly distributed within the circle.
20 * 
21 * @returns An array containing [x, y] coordinates of the random point
22 */
23function randPoint(): number[] {
24    // Generate random radius using square root for uniform distribution
25    // sqrt is used to ensure uniform distribution across the circle area
26    // Without sqrt, points would be more concentrated near the center
27    const randomRadius = Math.sqrt(Math.random() * radius * radius);
28  
29    // Generate random angle in radians (0 to 2π)
30    const randomAngle = Math.random() * 2 * Math.PI;
31  
32    // Convert polar coordinates to Cartesian coordinates
33    // x = center_x + r * cos(θ)
34    const x = xCenter + randomRadius * Math.cos(randomAngle);
35    // y = center_y + r * sin(θ)
36    const y = yCenter + randomRadius * Math.sin(randomAngle);
37  
38    return [x, y];
39}
40

Time and Space Complexity

Time Complexity: O(1)

  • The __init__ method simply stores three values, which takes O(1) time.
  • The randPoint method performs a fixed number of operations:
    • random.uniform() is called twice - O(1) each
    • math.sqrt() is called once - O(1)
    • math.cos() and math.sin() are called once each - O(1) each
    • Basic arithmetic operations (multiplication, addition, exponentiation) - O(1) each
  • All operations are constant time regardless of the radius or center values.

Space Complexity: O(1)

  • The __init__ method stores three floating-point values (radius, x_center, y_center) - O(1) space.
  • The randPoint method uses a fixed number of local variables (length, degree, x, y) - O(1) space.
  • No additional data structures that scale with input size are used.
  • The returned list contains exactly 2 elements regardless of input - O(1) space.

Common Pitfalls

1. Incorrect Radius Sampling (Most Critical)

Pitfall: The most common mistake is sampling the radius directly using random.uniform(0, radius) instead of using the square root transformation.

Why it's wrong:

  • Direct radius sampling causes points to cluster near the center
  • The area of a ring at distance r from center is proportional to r (circumference = 2πr)
  • Larger radii have more area, so they should have proportionally more points
  • Without the square root transformation, the distribution becomes biased toward the center

Incorrect implementation:

# WRONG - causes center clustering
random_radius = random.uniform(0, self.radius)  

Correct implementation:

# CORRECT - uniform distribution
random_radius = math.sqrt(random.uniform(0, self.radius ** 2))

2. Rejection Sampling Inefficiency

Pitfall: Using rejection sampling (generating points in a square and rejecting those outside the circle).

Why it's problematic:

  • Wastes approximately 21.5% of generated points (1 - π/4)
  • Non-deterministic runtime - could theoretically loop forever
  • Less efficient than the polar coordinate method

Inefficient approach:

# INEFFICIENT - rejection sampling
while True:
    x = random.uniform(-self.radius, self.radius)
    y = random.uniform(-self.radius, self.radius)
    if x**2 + y**2 <= self.radius**2:
        return [self.x_center + x, self.y_center + y]

3. Angle Unit Confusion

Pitfall: Mixing degrees and radians when generating the angle.

Wrong implementations:

# WRONG - using degrees with math.cos/sin (which expect radians)
random_angle = random.uniform(0, 360)
x = self.x_center + random_radius * math.cos(random_angle)  # Expects radians!

# WRONG - incorrect radian range
random_angle = random.uniform(0, 2)  # Should be 0 to 2π, not 0 to 2

Correct implementation:

random_angle = random.uniform(0, 2 * math.pi)  # Or random.uniform(0, 1) * 2 * math.pi

4. Forgetting Center Translation

Pitfall: Generating points around the origin (0, 0) instead of the actual circle center.

Wrong implementation:

# WRONG - forgets to add center coordinates
x = random_radius * math.cos(random_angle)  # Missing self.x_center
y = random_radius * math.sin(random_angle)  # Missing self.y_center

Correct implementation:

x = self.x_center + random_radius * math.cos(random_angle)
y = self.y_center + random_radius * math.sin(random_angle)

5. Edge Case: Zero Radius

Pitfall: Not handling the case when radius is 0 or very small.

Potential issue: While mathematically valid, a circle with radius 0 should always return the center point. The current implementation handles this correctly, but some alternative approaches might have numerical stability issues.

Complete Correct Solution:

def randPoint(self) -> List[float]:
    # Correct square root transformation for uniform distribution
    length = math.sqrt(random.uniform(0, self.radius**2))
    # Correct radian range: 0 to 2π
    degree = random.uniform(0, 2 * math.pi)
    # Correct translation to circle center
    x = self.x_center + length * math.cos(degree)
    y = self.y_center + length * math.sin(degree)
    return [x, y]
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More