A weighted version of random.choice
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
random.choice() selects an element uniformly at random — every element has equal probability. For weighted random selection where some elements are more likely than others, Python 3.6+ provides random.choices() with a weights parameter. For older Python versions or specialized needs, you can use numpy.random.choice(), bisect with cumulative weights, or build an alias table for O(1) sampling.
random.choices() — Python 3.6+ (Recommended)
Weights do not need to sum to 1 or 100 — Python normalizes them internally. [70, 20, 8, 2] means "common" has a 70% chance, "uncommon" 20%, "rare" 8%, "legendary" 2%.
Using cum_weights for Pre-Computed Cumulative Weights
Pass cum_weights instead of weights if you pre-compute them — avoids recalculating cumulative sums on each call.
numpy.random.choice() — For Large-Scale Sampling
NumPy requires probabilities that sum to 1.0, not arbitrary weights.
Manual Implementation with bisect (Python < 3.6)
This builds cumulative weights [70, 90, 98, 100], picks a random number in [0, 100), and uses binary search to find which bucket it falls in. Time complexity is O(n) to build cumulative weights and O(log n) per sample.
Real-World Examples
Loot Drop System
Weighted Load Balancing
A/B Testing with Unequal Split
Performance Comparison
For single picks, random.choices is faster (no NumPy overhead). For thousands of picks at once, NumPy wins.
Common Pitfalls
- Weights summing to zero: If all weights are 0,
random.choicesraisesValueError: Total of weights must be greater than zero. Validate weights before calling. - Confusing
weightswithcum_weights: Passing cumulative weights to theweightsparameter produces incorrect probabilities. Usecum_weightsexplicitly if your weights are already cumulative. random.choicesreturns a list: Even withk=1, the return value is a list["item"], not a single value. Userandom.choices(..., k=1)[0]to get the item directly.- NumPy probabilities not summing to 1.0:
numpy.random.choiceraisesValueErrorifpdoes not sum to approximately 1.0. Normalize withp = [w/sum(weights) for w in weights]or userandom.choiceswhich accepts unnormalized weights. - Sampling without replacement with weights:
random.choicesalways samples with replacement. For weighted sampling without replacement, usenumpy.random.choice(..., replace=False)or implement reservoir sampling.
Summary
- Use
random.choices(items, weights=weights, k=n)for weighted selection in Python 3.6+ - Weights do not need to sum to 1 — Python normalizes automatically
- Use
numpy.random.choice()for large-scale sampling or when probabilities must sum to 1.0 - For Python < 3.6, use
bisectwith cumulative weights for O(log n) sampling random.choicessamples with replacement; use NumPy for weighted sampling without replacement- Seed the random generator for reproducible results in testing and A/B experiments

