You are given a 0-indexed array of positive integers w
where w[i]
describes the weight of the ith
index.
You need to implement the function pickIndex()
, which randomly picks an index in the range [0, w.length - 1]
(inclusive) and returns it. The probability of picking an index i
is w[i] / sum(w)
.
w = [1, 3]
, the probability of picking index 0
is 1 / (1 + 3) = 0.25
(i.e., 25%
), and the probability of picking index 1
is 3 / (1 + 3) = 0.75
(i.e., 75%
).Example 1:
Input ["Solution","pickIndex"] [[[1]],[]] Output [null,0] Explanation Solution solution = new Solution([1]); solution.pickIndex(); // return 0. The only option is to return 0 since there is only one element in w.
Example 2:
Input ["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"] [[[1,3]],[],[],[],[],[]] Output [null,1,1,1,1,0] Explanation Solution solution = new Solution([1, 3]); solution.pickIndex(); // return 1. It is returning the second element (index = 1) that has a probability of 3/4. solution.pickIndex(); // return 1 solution.pickIndex(); // return 1 solution.pickIndex(); // return 1 solution.pickIndex(); // return 0. It is returning the first element (index = 0) that has a probability of 1/4. Since this is a randomization problem, multiple answers are allowed. All of the following outputs can be considered correct: [null,1,1,1,1,0] [null,1,1,1,1,1] [null,1,1,1,0,0] [null,1,1,1,0,1] [null,1,0,1,0,0] ...... and so on.
Constraints:
1 <= w.length <= 104
1 <= w[i] <= 105
pickIndex
will be called at most 104
times.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:
Imagine you have several boxes, each with a different size. The goal is to pick one box at random, but bigger boxes should have a higher chance of being picked. The brute force approach involves explicitly representing the larger boxes more often to reflect their higher probability.
Here's how the algorithm would work step-by-step:
import random
class RandomPickWithWeight:
def __init__(self, weights):
self.expanded_list = []
# Create the expanded list based on weights
for index, weight in enumerate(weights):
# Populate the list with index repeated by weight
for _ in range(weight):
self.expanded_list.append(index)
def pickIndex(self):
# Choose a random index from the expanded list
random_index = random.randint(0, len(self.expanded_list) - 1)
# Return element at randomly chosen index
return self.expanded_list[random_index]
We want to randomly pick something, but some things are more likely to be picked than others. The trick is to turn these different probabilities into a structure that makes picking easy and fast.
Here's how the algorithm would work step-by-step:
import random
class RandomPickWithWeight:
def __init__(self, weights):
self.cumulative_weights = []
total_weight = 0
# Calculating cumulative weights for each item
for weight in weights:
total_weight += weight
self.cumulative_weights.append(total_weight)
self.total_weight = total_weight
def pickIndex(self):
# Generate a random number within the total weight range
random_number = random.random() * self.total_weight
left, right = 0, len(self.cumulative_weights) - 1
# Binary search to find the index corresponding to the random number
while left <= right:
mid = (left + right) // 2
if self.cumulative_weights[mid] < random_number:
left = mid + 1
else:
right = mid - 1
# We return the left pointer, which will be the appropriate index
return left
Case | How to Handle |
---|---|
Null or empty weights array | Throw an IllegalArgumentException or return -1 to indicate invalid input. |
Weights array contains only one element | Always return index 0 since it's the only valid choice. |
Weights array with very large numbers leading to potential integer overflow in prefix sum calculation | Use long data type to store prefix sums to avoid overflow. |
Weights array contains all zero values | Throw an IllegalArgumentException or return -1, as no index can be selected proportionally. |
Weights array with very skewed distribution (one weight is significantly larger than others) | The prefix sum and binary search approach will still work, but the large weight will be picked more often. |
Maximum sized input array (limited by memory constraints) | Ensure that the prefix sum array does not exceed available memory, potentially optimizing data structures. |
Extreme boundary values for weights (e.g., Integer.MAX_VALUE) | Handle potential overflow when calculating prefix sums, potentially using long data type or modular arithmetic. |
Weights array contains negative numbers | Throw an IllegalArgumentException as weights should be positive. |