Random Number Generation
Reviewed & published by Brayan K
Random number generation in modern NumPy is handled by a Generator object, created with np.random.default_rng(), that produces random floats, integers, and samples from statistical distributions on demand.
Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll create a generator, draw random floats and integers, sample from a normal distribution, pick and shuffle elements, and lock in reproducible results with a fixed seed.
What You'll Learn in This Lesson
- Creating a generator with np.random.default_rng
- Random floats with rng.random
- Random integers with rng.integers
- Sampling a normal distribution with rng.normal
- Picking and shuffling with choice and shuffle
- Reproducibility with a fixed seed
1 The Modern Generator API
Modern NumPy creates randomness through a Generator object. You build one with np.random.default_rng(seed) and then call methods on it. Passing a fixed integer seed makes the sequence reproducible — the same seed always yields the same numbers.
import numpy as np
# Create a generator with a fixed seed for reproducibility
rng = np.random.default_rng(42)
# Random floats in [0, 1)
print(rng.random(3))
# [0.77395605 0.43887844 0.85859792]
# Run again with the SAME seed -> identical numbers
rng2 = np.random.default_rng(42)
print(rng2.random(3))
# [0.77395605 0.43887844 0.85859792]Expected output: with seed 42, both calls print the identical array [0.77395605 0.43887844 0.85859792]. Change the seed and the numbers change.
2 Random Integers with rng.integers
For whole numbers use rng.integers(low, high, size). Like Python's range, the high value is excluded by default. The size argument controls how many you get and can even be a tuple for a 2D grid.
import numpy as np
rng = np.random.default_rng(0)
# Five integers from 1 up to (but not including) 7 -> dice rolls
rolls = rng.integers(1, 7, size=5)
print(rolls) # e.g. [5 6 1 4 4] (reproducible with seed 0)
# A 2x3 grid of integers from 0 to 9
grid = rng.integers(0, 10, size=(2, 3))
print(grid)
# [[8 0 1]
# [3 3 7]]Expected output: five values in the range 1–6 and a 2×3 grid of values 0–9. With seed 0 these are reproducible; the exact numbers stay fixed as long as the seed does.
3 Sampling a Normal Distribution
Real-world data often follows a bell curve. rng.normal(mean, std, size) draws samples from a normal (Gaussian) distribution with the mean and standard deviation you choose. Drawing many samples, the average will sit close to the requested mean.
import numpy as np
rng = np.random.default_rng(7)
# 1000 samples centered at 100 with spread of 15 (like IQ scores)
samples = rng.normal(100, 15, size=1000)
print(samples.shape) # (1000,)
print(round(samples.mean(), 1)) # close to 100.0 (e.g. 100.4)
print(round(samples.std(), 1)) # close to 15.0 (e.g. 14.8)Expected output: shape (1000,), a mean near 100.0 and a standard deviation near 15.0. The exact decimals depend on the seed, but they cluster around those targets.
4 choice, shuffle, and the Legacy API
rng.choice picks random elements from an array, with or without replacement. rng.shuffle reorders an array in place. You may still see the older np.random.seed and np.random.rand in tutorials — they work, but the Generator API is preferred for new code.
import numpy as np
rng = np.random.default_rng(3)
colors = np.array(["red", "green", "blue", "yellow"])
# Pick 2 colors without repeats
print(rng.choice(colors, size=2, replace=False)) # e.g. ['blue' 'red']
# Shuffle a deck of numbers IN PLACE
deck = np.arange(1, 6)
rng.shuffle(deck)
print(deck) # e.g. [3 1 5 2 4]
# Legacy API (still works, but prefer default_rng)
np.random.seed(42)
print(np.random.rand(2)) # [0.37454012 0.95071431]Expected output: two distinct colors, a shuffled 1–5 deck, and from the legacy seed-42 call the fixed pair [0.37454012 0.95071431].
🎯 YOUR TURN: Fill in the Blanks
Replace each ___ so the program creates a seeded generator and rolls four dice.
import numpy as np
# Create a reproducible generator with seed 99
rng = np.random.___(99) # fill in the function name
# Four dice: integers 1 through 6 (high value 7 is excluded)
dice = rng.integers(1, ___, size=4) # fill in the high value
print(dice) # four numbers, each between 1 and 6
print(dice.min() >= 1 and dice.max() <= 6) # Expected output: TrueExpected output: four numbers from 1 to 6 and True. (Answers: default_rng, 7.)
⚠️ Common Errors & Quick Reference
❌ Results are different every run when I wanted them fixed
You created the generator without a seed.
✅ Fix: pass a fixed integer, e.g. np.random.default_rng(42), for reproducible output.
❌ rng.integers gave a number equal to high
It never should — high is excluded by default, so this usually means an off-by-one expectation.
✅ Fix: to include the top value, pass endpoint=True, or set high one above your target.
| Task | Code |
|---|---|
| Make a seeded generator | np.random.default_rng(42) |
| Random floats [0, 1) | rng.random(5) |
| Random integers | rng.integers(0, 10, size=5) |
| Normal distribution | rng.normal(0, 1, size=5) |
| Pick random elements | rng.choice(arr, size=2) |
| Shuffle in place | rng.shuffle(arr) |
🏆 Mini Challenge: Simulate Coin Flips
Flip a fair coin 1000 times and count the heads. With enough flips the proportion should sit near 0.5.
import numpy as np
rng = np.random.default_rng(2024)
# 0 = tails, 1 = heads
flips = rng.integers(0, 2, size=1000)
heads = flips.sum()
print("Heads:", heads) # e.g. Heads: 503
print("Proportion:", heads / 1000) # close to 0.5❓ Frequently Asked Questions
Lesson complete — randomness, under control!
You can build a seeded generator with default_rng, draw floats, integers, and normal samples, pick and shuffle elements, and guarantee reproducible results with a fixed seed.
Practice quiz
How do you create a generator in modern NumPy?
- np.random.default_rng(seed)
- np.random.make()
- np.Generator()
- np.rng()
Answer: np.random.default_rng(seed). np.random.default_rng(seed) is the recommended way to create a Generator.
How do you make random results reproducible?
- Call shuffle twice
- Pass a fixed integer seed to default_rng
- Use a larger array
- Never call random()
Answer: Pass a fixed integer seed to default_rng. A fixed seed always produces the same sequence, essential for reproducibility.
What does rng.random() return?
- Integers
- Booleans
- Floats in the half-open interval [0, 1)
- Strings
Answer: Floats in the half-open interval [0, 1). rng.random returns floats uniformly distributed in [0, 1).
By default, is the high value excluded in rng.integers(low, high)?
- No, it is always included
- Only for size>1
- Only for negative numbers
- Yes, high is excluded by default
Answer: Yes, high is excluded by default. Like Python's range, rng.integers excludes high by default unless endpoint=True.
Which method draws samples from a normal distribution?
- rng.normal(mean, std, size)
- rng.integers
- rng.random
- rng.choice
Answer: rng.normal(mean, std, size). rng.normal(mean, std, size) draws Gaussian samples with the given mean and spread.
What does rng.shuffle(arr) do?
- Returns a sorted copy
- Reorders the array in place and returns None
- Returns a new shuffled array
- Picks one element
Answer: Reorders the array in place and returns None. rng.shuffle reorders the array in place and returns None.
Which method picks random elements from an array?
- rng.normal
- rng.integers
- rng.choice
- rng.shuffle
Answer: rng.choice. rng.choice selects random elements, with or without replacement.
Which is the legacy global API you may still see?
- rng.integers
- default_rng
- rng.normal
- np.random.seed and np.random.rand
Answer: np.random.seed and np.random.rand. np.random.seed/np.random.rand are the older global functions; default_rng is preferred.
Two generators created with the same seed will...
- Produce the same sequence of numbers
- Produce opposite numbers
- Always differ
- Raise an error
Answer: Produce the same sequence of numbers. The same seed yields the identical sequence, which is what makes results reproducible.
How do you include the top value in rng.integers?
- Set size larger
- Pass endpoint=True
- Use rng.random instead
- Subtract one from low
Answer: Pass endpoint=True. Pass endpoint=True (or set high one above your target) to include the top value.
Continue this course
- Previous: Copies vs Views
- Next: Linear Algebra with numpy.linalg — Solve systems, find determinants, inverses, and eigenvalues
- Quick reference: NumPy cheat sheet › Linear Algebra & Random
Frequently asked questions
How do I generate random numbers in modern NumPy?
Create a Generator with rng = np.random.default_rng(seed), then call methods on it such as rng.random() for floats in [0, 1), rng.integers(low, high, size) for integers, and rng.normal(mean, std, size) for normally distributed values. This Generator API is the recommended approach in NumPy 1.17 and later.
How do I make random results reproducible?
Pass a fixed integer seed when you create the generator, like np.random.default_rng(42). The same seed always produces the same sequence of random numbers, which is essential for debugging, testing, and reproducible experiments. Use a different seed (or no seed) when you want different results each run.
Should I use np.random.seed and np.random.rand?
Those are the legacy global functions. They still work and you will see them in older code, but NumPy now recommends the Generator API via np.random.default_rng because it avoids hidden global state and produces higher-quality random numbers. Prefer default_rng for new code.
What is the difference between rng.random and rng.integers?
rng.random returns floating-point numbers uniformly distributed in the half-open interval [0, 1). rng.integers returns whole numbers from a range you specify with low and high. By default rng.integers excludes the high value, matching Python's range behavior, unless you pass endpoint=True.