478. Generate Random Point in a Circle
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 circlex_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:
- 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. - Random angle: Uses
degree = random.uniform(0, 1) * 2π
to get a random angle from 0 to 2π radians. - 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 r²
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.
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:
- Generate a uniform random value
u
between 0 and 1 - Set
u = r²/R²
- 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:
-
Generate random radius with proper distribution:
random.uniform(0, self.radius**2)
generates a uniform random value between 0 andradius²
math.sqrt()
transforms this to ensure uniform area distribution- This gives us
length
, the distance from center to our random point
-
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
-
Convert polar to Cartesian coordinates:
x = self.x_center + length * math.cos(degree)
calculates the x-coordinatey = 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
-
Return the result:
- Return the point as a list
[x, y]
- Return the point as a list
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 EvaluatorExample 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()
:
-
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
- Suppose
-
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
- Suppose
-
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 takesO(1)
time. - The
randPoint
method performs a fixed number of operations:random.uniform()
is called twice -O(1)
eachmath.sqrt()
is called once -O(1)
math.cos()
andmath.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]
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!