You are given a 0-indexed array of positive integers w
where w[i]
describes the weight of the i<sup>th</sup>
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 <= 10^4
1 <= w[i] <= 10^5
pickIndex
will be called at most 10^4
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:
The basic idea is to simulate the picking process many, many times. We will directly use the given weights to guide our random selection in each simulated pick, until we get our answer.
Here's how the algorithm would work step-by-step:
import random
class RandomPickWithWeight:
def __init__(self, weights):
self.weights = weights
def pickIndex(self):
# Calculate the total weight to define the 'bag' size
total_weight = sum(self.weights)
# Generate a random number within the range of the total weight
random_number = random.randint(1, total_weight)
current_weight_sum = 0
# Iterate through the weights to find the corresponding index
for index, weight in enumerate(self.weights):
current_weight_sum += weight
# Check if the random number falls within the current weight range
if random_number <= current_weight_sum:
return index
This problem asks us to pick a random item, but not with equal chances. Items with higher weights should be picked more often. We can solve it by creating a range for each item based on its weight and then figuring out which range a randomly generated number falls into.
Here's how the algorithm would work step-by-step:
import random
class Solution:
def __init__(self, weights):
self.weights = weights
self.cumulative_sum_of_weights = []
total_weight = 0
for weight in weights:
total_weight += weight
self.cumulative_sum_of_weights.append(total_weight)
self.total_weight = total_weight
def pickIndex(self):
# Generate a random number within the total weight range.
random_number = random.randint(1, self.total_weight)
left = 0
right = len(self.weights) - 1
# Binary search to find the index corresponding to random number.
while left < right:
mid = left + (right - left) // 2
if self.cumulative_sum_of_weights[mid] < random_number:
left = mid + 1
else:
right = mid
# Return the index where the random number falls.
return left
Case | How to Handle |
---|---|
Null or empty weights array | Throw an IllegalArgumentException or return -1 indicating an invalid state, as no index can be chosen. |
weights array with a single element | Return 0 since it's the only possible index with 100% probability. |
All weights are zero | Throw an IllegalArgumentException or return -1 as it's impossible to pick an index proportionally. |
Large weights causing potential integer overflow during cumulative sum calculation | Use long data type for cumulative sums to avoid integer overflow. |
Maximum-sized weights array (performance consideration) | Use binary search on the cumulative weights to ensure logarithmic time complexity for picking an index. |
Weights array containing extremely small positive values approaching zero | The algorithm should work as intended since weights are positive, but consider possible floating-point precision issues if using floats/doubles (should be ints). |
Weights array with identical values | The algorithm should still correctly pick an index with uniform probability, as the cumulative weights will increase linearly. |
Edge case of input array exceeding memory limits | Consider using an alternative approach that doesn't require storing the entire cumulative sum array if memory is constrained. |