Random Pick with Weight
Problem
Given a list of positive integer weights, implement a function that randomly picks an index with probability proportional to its weight.
- 1 ≤ w.length ≤ 10⁴
- 1 ≤ w[i] ≤ 10⁵
- pickIndex will be called at most 10⁴ times
Example
w = [1, 3]0 with probability 1/4, 1 with probability 3/4The total weight is 4. The algorithm first computes prefix sums: [1, 4]. When pickIndex is called, it generates a random integer target between 1 and 4 inclusive. If target is 1, it returns index 0; if target is 2, 3, or 4, it returns index 1. This ensures the probability of returning index i is proportional to w[i]. The critical moment is using binary search on the prefix sums to efficiently find the correct index for the random target.
Approach
Straightforward Solution
A naive approach would generate a random number and scan through the weights cumulatively to find the corresponding index, resulting in O(n) time per pickIndex call, which is inefficient for large inputs or many calls.
Core Observation
The probability of picking an index i is w[i] divided by the total sum of weights. This can be modeled as selecting a random number in the range [1, total_weight] and finding which prefix sum interval it falls into.
Path to Optimal
PreviewBy precomputing prefix sums, the problem reduces to searching for the smallest prefix sum greater than or equal to the random target. Using binary search on the prefix sums array reduces the pickIndex call time to O(log n)…
Full step-by-step walkthrough on Pro →
Optimal Approach
PreviewPrecompute prefix sums of the weights during initialization. For each pickIndex call, generate a random integer target between 1 and total weight inclusive…
Full step-by-step walkthrough on Pro →
Want the full reasoning chain?
Unlock the complete walkthrough, line-by-line analysis, and recall drill.
Unlock ProTime
O(n) for initialization, O(log n) per pickIndex call
Computing prefix sums requires a single pass through the weights (O(n)). Each pickIndex call uses binary search on the prefix sums array, which takes O(log n) time.
Space
O(n)
The prefix sums array stores cumulative sums for all weights, requiring O(n) auxiliary space proportional to the input size.
Pattern Spotlight
Binary Search on Prefix Sums for Weighted Random Selection
Transform weighted probabilities into prefix sums and use binary search to efficiently map a random target to its corresponding weight interval, enabling O(log n) selection time.
Solution
| 1 | class Solution: |
| 2 | def __init__(self, w: list[int]): |
| 3 | self.prefix = [] |
| 4 | total = 0 |
| 5 | |
| 6 | for weight in w: |
| 7 | total += weight |
| 8 | self.prefix.append(total) |
| 9 | |
| 10 | self.total = total |
| 11 | |
| 12 | def pickIndex(self) -> int: |
| 13 | target = random.randint(1, self.total) |
| 14 | |
| 15 | left = 0 |
| 16 | right = len(self.prefix) - 1 |
| 17 | |
| 18 | while left < right: |
| 19 | mid = (left + right) // 2 |
| 20 | |
| 21 | if self.prefix[mid] < target: |
| 22 | left = mid + 1 |
| 23 | else: |
| 24 | right = mid |
| 25 | |
| 26 | return left |
Step-by-Step Solution
Build Prefix Sum Array to Represent Weight Intervals
| 3 | self.prefix = [] |
| 4 | total = 0 |
| 6 | for weight in w: |
| 7 | total += weight |
| 8 | self.prefix.append(total) |
| 10 | self.total = total |
Objective
To create a prefix sums array that maps each index to the cumulative weight up to that index.
Key Insight
Prefix sums convert the discrete weights into contiguous intervals on the number line from 1 to total weight. This transformation allows the problem of weighted random selection to become a search for the interval containing a random target number. The prefix sums array is the foundation for efficient binary search.
Interview Quick-Check
Core Logic
Prefix sums represent cumulative weights, enabling mapping from a random target to an index by interval containment.
Common Pitfalls & Bugs
Forgetting to accumulate weights correctly or misaligning prefix sums with indices can cause incorrect interval mapping.
Generate Random Target and Use Binary Search to Find Corresponding Index
To pick a random integer in the total weight range and locate its corresponding index using binary search on prefix sums.
1 more step with full analysis available on Pro.
Line Analysis
This solution has 5 Critical lines interviewers watch for.
return left
Return the left pointer as the index corresponding to the target.
After binary search completes, left points to the smallest prefix sum >= target, which maps to the weighted random index to return.
self.prefix.append(total)
Append the current total to the prefix sums list.
Storing cumulative sums creates intervals that represent the weighted ranges for each index, which are essential for binary search.
target = random.randint(1, self.total)
Generate a random integer target between 1 and total weight inclusive.
Randomly selecting a target in this range simulates picking an index with probability proportional to its weight by mapping the target to prefix sum intervals.
Full line-by-line criticality + rationale for all 16 lines available on Pro.
Test Your Understanding
Why is binary search used on the prefix sums array instead of a linear scan?
See the answer with Pro.
Related Problems
Modified Binary Search pattern
Don't just read it. Drill it.
Reconstruct Random Pick with Weight from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Random Pick with Weight drill