You are given an integer n and an array of unique integers blacklist. Design an algorithm to pick a random integer in the range [0, n - 1] that is not in blacklist. Any integer that is in the mentioned range and not in blacklist should be equally likely to be returned.
Optimize your algorithm such that it minimizes the number of calls to the built-in random function of your language.
Implement the Solution class:
Solution(int n, int[] blacklist) Initializes the object with the integer n and the blacklisted integers blacklist.int pick() Returns a random integer in the range [0, n - 1] and not in blacklist.Example 1:
Input
["Solution", "pick", "pick", "pick", "pick", "pick", "pick", "pick"]
[[7, [2, 3, 5]], [], [], [], [], [], [], []]
Output
[null, 0, 4, 1, 6, 1, 0, 4]
Explanation
Solution solution = new Solution(7, [2, 3, 5]);
solution.pick(); // return 0, any integer from [0,1,4,6] should be ok. Note that for every call of pick,
// 0, 1, 4, and 6 must be equally likely to be returned (i.e., with probability 1/4).
solution.pick(); // return 4
solution.pick(); // return 1
solution.pick(); // return 6
solution.pick(); // return 1
solution.pick(); // return 0
solution.pick(); // return 4
Constraints:
1 <= n <= 1090 <= blacklist.length <= min(105, n - 1)0 <= blacklist[i] < nblacklist are unique.2 * 104 calls will be made to pick.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
We need to pick a random number from a range, but certain numbers are forbidden. The brute force method is like randomly guessing numbers until we find one that's allowed. We keep trying until we succeed, ignoring the forbidden ones.
Here's how the algorithm would work step-by-step:
import random
def pick_with_blacklist_brute_force(upper_bound, blacklist):
while True:
# Generate a random integer within the specified range.
random_number = random.randint(0, upper_bound)
# Check if the generated number is in the blacklist.
if random_number in blacklist:
# Discard the number and try again.
continue
# If the number is not blacklisted, return it.
return random_numberThe goal is to pick a random number from a range, but some numbers are off-limits. Instead of repeatedly guessing and checking, we'll remap the valid numbers to a smaller, more manageable range, and then pick randomly from that smaller range.
Here's how the algorithm would work step-by-step:
import random
class Solution:
def __init__(self, upper_bound: int, blacklist: list[int]):
self.valid_number_count = upper_bound - len(blacklist)
self.number_map = {}
self.upper_bound = upper_bound
self.blacklist = blacklist
# Add blacklist numbers to set for quick lookup.
self.blacklist_set = set(blacklist)
# Map larger blacklisted numbers to valid range.
for blacklisted_number in blacklist:
if blacklisted_number < self.valid_number_count:
while upper_bound - 1 in self.blacklist_set:
upper_bound -= 1
self.number_map[blacklisted_number] = upper_bound - 1
upper_bound -= 1
def pick(self) -> int:
# Generate random number within valid range.
random_index = random.randint(0, self.valid_number_count - 1)
# If the random number has been remapped, return its mapping.
if random_index in self.number_map:
return self.number_map[random_index]
# Otherwise return the number directly.
return random_index| Case | How to Handle |
|---|---|
| n is equal to blacklist size | Return an empty list because all possible numbers are blacklisted. |
| Blacklist is empty | The uniform random number generation is used to generate the number from 0 to n-1 without any checks. |
| Blacklist contains duplicate numbers | The algorithm overwrites values in the map, effectively only considering the last occurrence of each duplicate in the blacklist. |
| Blacklist contains numbers outside the range [0, n) | Filter out these numbers, as they don't affect the valid pick range and could cause out of bounds errors. |
| n is a large number (e.g., close to Integer.MAX_VALUE) | Ensure that the data structures (e.g., HashMap) used to store the mapping can handle a large number of entries without exceeding memory limits or causing performance issues; use long if necessary. |
| All numbers from 0 to n-1 except one are blacklisted. | The algorithm should still correctly return the single non-blacklisted number. |
| The random number generator produces the same sequence of numbers repeatedly. | This is a property of the random number generator itself and should be addressed with a good random number generator seed; the algorithm itself should function correctly, just perhaps not randomly. |
| Integer overflow when calculating n - blacklist.length | Use long data type for calculations involving n and blacklist length to prevent overflow. |