Problem
You are given an array of non-overlapping axis-aligned rectangles rects where rects[i] = [ai, bi, xi, yi] indicates that (ai, bi) is the bottom-left corner point of the ith rectangle and (xi, yi) is the top-right corner point of the ith rectangle. Design an algorithm to pick a random integer point inside the space covered by one of the given rectangles. A point on the perimeter of a rectangle is included in the space covered by the rectangle.
Any integer point inside the space covered by one of the given rectangles should be equally likely to be returned.
Note that an integer point is a point that has integer coordinates.
Implement the Solution class:
Solution(int[][] rects)Initializes the object with the given rectanglesrects.int[] pick()Returns a random integer point[u, v]inside the space covered by one of the given rectangles.
Examples
Example 1:
| |
Solution
Caution - Randomly Picking Rectangle and Then Point in it Will Not Work
An intuitive solution is to randomly select one rectangle from the rects and then generate a random point within it. However, this method is flawed. Why?
Consider two rectangles, R1 and R2, where R1 has a larger area than R2.
Let S1 represent the set of points within rectangle R1, and S2 the set of points within rectangle R2. Mathematically, we have:
| |
It is clear that R1 has a larger area than R2, so S1 contains more points than S2 (i.e., n > m).
To randomly pick an integer point within the area covered by one of the given rectangles, simply selecting a rectangle randomly won’t work; the area of each rectangle must be considered.
Method 1 - Pick Rectangle First Using Area and TreeMap and Then Point using Using TreeMap.ceilingKey()
We can do following:
map: Stores cumulative area as keys and rectangle indices as values.rects: Stores the input array of rectangles.totalArea: Stores the sum of the areas of all rectangles.rnd: ARandomobject for generating random numbers.
Now, we can follow these steps:
- Initialize
rects: Store the input array of rectangles. - Initialize
map: Create a newTreeMap. - Initialize
totalArea: Set the total area to 0. - Calculate Areas and Populating the Map:
- Iterate over each rectangle to calculate its area.
- Update
totalAreato include the area of the current rectangle. - Map the cumulative area to the rectangle’s index in the
map.
- Generate Random Area Index:
- Generate a random number between 0 and
totalArea - 1, then add 1 to shift the range to [1,totalArea]. - Use
ceilingKeyto find the smallest key in the map greater than or equal to this random number (c). This key points to the rectangle that should be picked.
- Generate a random number between 0 and
- Pick a Random Point in the Rectangle:
- Retrieve the rectangle index from the
mapusingc. - Call
pickInRectwith the selected rectangle to return a random point within it.
- Retrieve the rectangle index from the
Code
| |
Complexity
Constructor: Solution(int[][] rects)
- ⏰ Time complexity:
O(n log n) - 🧺 Space complexity:
O(n)
Method: int[] pick()
- ⏰ Time complexity:
O(log n) - 🧺 Space complexity:
O(1)
Method 2 - Pick Rectangle First Using Area and TreeMap and Then Point using Using TreeMap.floorKey()
We are also saving rectangle in the map, instead of saving rects array. Here are the steps:
Class Initialization:
- Define a
TreeMap<Integer, int[]> areaToRectMapto map cumulative areas to rectangles. - Initialize
totalAreato 0. - Initialize a
Randomobjectrandfor generating random numbers.
- Define a
Constructor:
- Initialize
areaToRectMap,totalArea, andrand. - Call
buildMap()to populate theareaToRectMap.
- Initialize
buildMap Method:
- Iterate through each rectangle in
rects. - Calculate the area of each rectangle.
- Map the cumulative area to the rectangle in
areaToRectMap. - Update
totalAreawith the area of the current rectangle.
- Iterate through each rectangle in
pick Method:
- Generate a random integer
currIdxbetween 0 andtotalArea - 1. - Find the largest key in
areaToRectMapless than or equal tocurrIdxusingfloorKey. - Retrieve the rectangle associated with this key.
- Call
getRandomPointto generate a random point within the selected rectangle.
- Generate a random integer
getRandomPoint Method:
- Generate a random x-coordinate within the rectangle’s x-range.
- Generate a random y-coordinate within the rectangle’s y-range.
- Return the coordinates as an array.
Code
| |
Complexity
Constructor: Solution(int[][] rects)
- ⏰ Time complexity:
O(n log n) - 🧺 Space complexity:
O(n)
Method: int[] pick()
- ⏰ Time complexity:
O(log n) - 🧺 Space complexity:
O(1)
Method 3 - Reusing Area to Pickup the Point in Rectangle:
Also, we are saving rect[] inside the map, rather than the index earlier. Here are the steps:
- Class Initialization:
- Define a
TreeMap<Integer, int[]> mapto map cumulative areas to rectangles. - Initialize
totalAreato zero. - Create a
Randomobjectrandfor generating random numbers.
- Define a
- Constructor:
- Iterate over each rectangle in the
rectsarray:- Calculate the area of the rectangle.
- Add the rectangle’s cumulative area to
totalArea. - Store the cumulative area as the key and the rectangle as the value in
map.
- Iterate over each rectangle in the
- pick Method:
- Generate a random integer
randAreabetween 0 andtotalArea - 1. - Use
higherKeyto find the smallest key greater thanrandAreainmap. - Retrieve the rectangle associated with this key from
map. - Compute the difference between the selected cumulative area and
randArea. - Calculate the width of the rectangle.
- Determine random x and y coordinates within the rectangle using the difference.
- Generate a random integer
Code
| |
Complexity
Constructor: Solution(int[][] rects)
- ⏰ Time complexity:
O(n log n) - 🧺 Space complexity:
O(n)
Method: int[] pick()
- ⏰ Time complexity:
O(log n) - 🧺 Space complexity:
O(1)