Random numbers & simulation with Generator

Random numbers power synthetic datasets, sampling, train/test splits, bootstrapping and simulations of uncertain business outcomes. Modern NumPy code uses a Generator object created with np.random.default_rng(), not the legacy np.random.seed / np.random.rand functions.

Creating a Generator

import numpy as np

rng = np.random.default_rng(42)     # the seed makes results reproducible

rng.random(3)                       # floats in [0, 1)
rng.integers(1, 7, size=10)         # dice rolls: 1..6 (high is exclusive)
rng.normal(loc=500, scale=80, size=5)   # mean 500, std 80
rng.choice(["UPI", "Card", "Cash"], size=8, p=[0.6, 0.25, 0.15])
rng.permutation(10)                 # shuffled 0..9

Pass the rng object into your functions instead of creating new generators everywhere. That keeps a whole analysis reproducible from one seed.

Picking a distribution

Distribution Method Models
Uniform rng.uniform(a, b) Equal chance anywhere in a range
Normal rng.normal(mu, sigma) Heights, measurement errors
Poisson rng.poisson(lam) Customers arriving per hour
Binomial rng.binomial(n, p) Conversions out of n visitors
Exponential rng.exponential(scale) Time between arrivals
Lognormal rng.lognormal(mean, sigma) Order values (skewed, positive)

Monte Carlo: will we hit the target?

A tea stall chain wants to know the chance of crossing ₹30 lakh revenue next month. Daily customers per outlet are roughly Poisson with mean 260, and average spend is uncertain (normal, mean ₹38, std ₹4). There are 10 outlets and 30 days.

rng = np.random.default_rng(2025)
n_sims = 20_000

customers = rng.poisson(lam=260, size=(n_sims, 10, 30))
spend = rng.normal(38, 4, size=(n_sims, 1, 1))        # one spend level per simulation
revenue = (customers * spend).sum(axis=(1, 2))

print(f"Median revenue: ₹{np.median(revenue):,.0f}")
print(f"P(revenue >= 30 lakh): {(revenue >= 3_000_000).mean():.1%}")
print("90% interval:", np.percentile(revenue, [5, 95]).round(-3))

Instead of one optimistic number, you get a distribution of outcomes and a probability, which is far more useful for planning.

Bootstrapping a confidence interval

Resample your data with replacement many times to see how much a statistic could vary:

bills = rng.lognormal(mean=6, sigma=0.5, size=400)     # sample of 400 bill values
idx = rng.integers(0, bills.size, size=(5000, bills.size))
boot_means = bills[idx].mean(axis=1)
print(np.percentile(boot_means, [2.5, 97.5]))

Independent streams

For parallel simulations, create independent generators with rng.spawn(4) (NumPy 1.25+), so workers do not share or overlap random streams.

Common mistakes

  • Mixing legacy np.random.seed() with Generator code and expecting reproducibility.
  • Forgetting that rng.integers(1, 6) never returns 6.
  • Running too few simulations; check that results are stable when you double n_sims.
  • Using a normal distribution for values that cannot be negative, such as prices or waiting times.

Practice

Simulate a Diwali sale where 5,000 visitors each buy with probability 0.08 and order value is lognormal. Run 10,000 simulations and report the median revenue and the probability that revenue exceeds ₹10 lakh. Verify that rerunning with the same seed gives identical output.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India