random.binomialvariate

Mathematically sum(random() < p for i in range(n)), but fast for any n: it needs about n * p draws for small n * p and a constant number (BTRS rejection) for large ones. So it does NOT give the same numbers as the sum() loop.

random functionPython 3.12+Live demo
Common call
random.binomialvariate(10, 0.5)
Returns
int from 0 to n
Replaces
sum(random.random() < p for _ in range(n))
Watch out
Python 3.12+ only; p outside [0, 1] raises ValueError
random.binomialvariate(nn — Number of trials, >= 0.type: int · default: 1=1, pp — Success probability per trial, 0.0 <= p <= 1.0. p = 0 and p = 1 return 0 and n without drawing.type: float · default: 0.5=0.5)
→ int

Demo

Live evaluation
Eight binomial counts. Small n * p uses the geometric method, large n * p the BTRS method.
Try:
Inputs
seedint | stran int or a string
ninttrials
pfloatsuccess probability
Code
import random
random.seed(42)
[random.binomialvariate(10, 0.5) for _ in range(8)]
Result
[3, 5, 3, 5, 6, 7, 6, 3]

For p > 0.5 it counts failures instead (n - binomialvariate(n, 1 - p)). With n * p < 10 it jumps from success to success with geometric gaps computed by log2; otherwise it uses Hörmann's BTRS rejection method with lgamma, so even a million trials cost only a few draws. The estimate tab approaches the exact probability of 5+ successes, about 0.42 for p = 0.6.

Parameters

NameTypeRequiredDescription
nintno (1)Number of trials, >= 0.
pfloatno (0.5)Success probability per trial, 0.0 <= p <= 1.0. p = 0 and p = 1 return 0 and n without drawing.

Return value

int — Number of successes, 0 <= X <= n; mean n * p.

Common patterns

Defective items in a batch
How many of 500 parts fail if each fails with probability 2%?
import random
failures = random.binomialvariate(500, 0.02)
Simulated A/B conversions
Conversions for each variant over a day of traffic.
import random
conv_a = random.binomialvariate(visitors, 0.031)
conv_b = random.binomialvariate(visitors, 0.034)
Fallback before 3.12
Same distribution (different numbers), O(n) time.
import random
successes = sum(random.random() < p for _ in range(n))

Examples

1. 10 coin flips, 8 times
import random random.seed(42) [random.binomialvariate(10, 0.5) for _ in range(8)]
Returns
[3, 5, 3, 5, 6, 7, 6, 3]
2. Rare events
import random random.seed(42) [random.binomialvariate(100, 0.03) for _ in range(8)]
Returns
[1, 2, 3, 0, 2, 3, 2, 3]
3. A million trials, instantly
import random random.seed(42) [random.binomialvariate(1000000, 0.5) for _ in range(3)]
Returns
[500201, 499664, 500356]
4. Defaults n=1, p=0.5: a coin
import random random.seed(42) [random.binomialvariate() for _ in range(10)]
Returns
[0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
5. Mean is n * p
import random random.seed(42) round(sum(random.binomialvariate(20, 0.25) for _ in range(10000)) / 10000, 1)
Returns
5.0
6. p = 0 and p = 1 are allowed
import random random.seed(42) [random.binomialvariate(5, 0.0), random.binomialvariate(5, 1.0)]
Returns
[0, 5]
7. p outside [0, 1]
import random random.binomialvariate(10, 1.5)
Returns
ValueError: p must be in the range 0.0 <= p <= 1.0

Pitfalls

1. Expecting the same numbers as the sum() loop
The docs call it "mathematically equivalent" to sum(random() < p for i in range(n)): same distribution, different algorithm, different draws.
compare with sum()
import random
random.seed(42)
random.binomialvariate(10, 0.5) == sum(random.random() < 0.5 for _ in range(10))
False
compare distributions
import random
random.seed(42)
round(sum(random.binomialvariate(20, 0.25) for _ in range(10000)) / 10000, 1) == 20 * 0.25
True
2. Negative n
n is a count of trials; negative values are rejected rather than treated as 0.
n = -1
import random
random.seed(42)
random.binomialvariate(-1)
ValueError: n must be non-negative
n = 0
import random
random.seed(42)
random.binomialvariate(0)
0

When to use

Use it
  • Counting successes: defects, conversions, heads, hits
  • Large n where summing random() < p would be slow
Reach for something else
  • Python 3.11 and older → sum(random.random() < p for _ in range(n))
  • Which trials succeeded (not just how many) → [random.random() < p for _ in range(n)]
  • Weighted categories → choices()

Notes

CPython impl
Lib/random.py (3.12+): edge cases p == 0 / p == 1 return 0 / n; n == 1 returns int(random() < p); p > 0.5 uses symmetry; n * p < 10 uses Devroye's geometric method with math.log2; otherwise Hörmann's BTRS transformed rejection with math.lgamma
Platforms
The result is an int, but the algorithm compares floats computed with log2, log and lgamma (C library). A last-digit difference between platforms only matters if it flips a comparison, which is very rare
Errors
ValueError: n must be non-negative / p must be in the range 0.0 <= p <= 1.0

FAQ

In Python 3.12+ use random.binomialvariate(n, p). In older versions sum(random.random() < p for _ in range(n)) gives the same distribution, or use numpy.random.Generator.binomial.