Randomness & Seeds
What 'pseudo-random' really means, how np.random.seed() makes random results reproducible, and why every data scientist sets a seed.
Watch the lesson
1 video on CodeWithMunnaX
Before the IPL auction, an analyst wants to simulate 1,000 seasons to see how often a team reaches the playoffs. Before real ball-by-ball data arrives, a developer wants fake scores to test a dashboard. Both need random numbers — and both need to get the same random numbers again tomorrow, so the results can be checked and shared. NumPy's np.random module solves both problems.
Definition
A pseudo-random number generator (PRNG) is an algorithm that produces a long sequence of numbers that look random, but is completely determined by a starting value called the seed. Same seed → same sequence, every time, on every computer.
- Pseudo means "fake". A computer is a deterministic machine — it cannot flip a real coin. Instead it runs a formula: each new number is calculated from the generator's internal state, and the state is updated after every draw.
- Looks random means the numbers pass statistical tests: no visible pattern, evenly spread, no correlation between neighbours. For simulation and data science this is exactly what we need.
- Seed is the number that sets the starting state. If you never set it, NumPy picks one from the operating system (based on things like the current time), so every run is different.
Reproducibility means anyone who runs your code gets exactly the same output. In data science it matters for debugging ("the bug happens with seed 42"), for sharing results, for fair model comparisons and for grading exercises.
Syntax
python
np.random.seed(seed) # legacy global seed
rng = np.random.default_rng(seed) # modern way (see the Generator topic)| Parameter | Meaning | Default |
|---|---|---|
seed | Any non-negative integer (e.g. 0, 42, 2024). Same number → same sequence. | None → fresh unpredictable seed from the OS |
np.random.seed() returns None; it only changes the hidden global state that functions like np.random.rand() and np.random.randint() read from.
Example — the same seed rewinds the sequence
python
import numpy as np
np.random.seed(42)
print(np.random.randint(1, 100, size=5))
print(np.random.randint(1, 100, size=5))
np.random.seed(42) # rewind to the start of the same sequence
print(np.random.randint(1, 100, size=5))
print(np.random.randint(1, 100, size=5))text
[52 93 15 72 61]
[21 83 87 75 75]
[52 93 15 72 61]
[21 83 87 75 75]How it works: after seed(42), the generator is at "position 0" of a fixed sequence. The first call reads 5 numbers, the second call reads the next 5 (so the two lines differ). Calling seed(42) again jumps back to position 0, so the next two calls repeat exactly. A seed does not make every call identical — it makes the whole sequence repeatable.
Example — different seeds, different sequences
python
np.random.seed(7)
a = np.random.randint(0, 200, size=4)
np.random.seed(8)
b = np.random.randint(0, 200, size=4)
print("seed 7:", a)
print("seed 8:", b)text
seed 7: [175 196 25 67]
seed 8: [195 84 105 133]The seed value itself has no meaning — 8 is not "a bit more random" than 7. It is just a label for one particular sequence. 42 is popular only because of a famous book joke.
How a seeded run flows
text
seed(42) ──► state S0 ──draw──► 52 ──► state S1 ──draw──► 93 ──► state S2 ──► ...
seed(42) ──► state S0 ──draw──► 52 ──► ... (exactly the same path again)Where to put the seed
- Set it once, at the top of a notebook or script, right after
import numpy as np. - Re-running only part of a notebook continues the sequence from wherever it was, so outputs change. Re-run from the top (or re-seed in that cell) to get the original numbers back.
- In real projects prefer
rng = np.random.default_rng(42)and passrngaround — it avoids a hidden global state (covered later in this module).
Common mistakes
- ❌ Expecting two calls after one
seed()to give the same numbers. ✅ The seed fixes the sequence; each call moves forward through it. - ❌ Calling
np.random.seed(42)inside a loop — every iteration then produces identical "random" data. ✅ Seed once, before the loop. - ❌ Thinking a seed makes data "less random" or biased. ✅ The numbers are just as well-spread; they are only repeatable.
- ❌ Using
np.randomfor passwords, OTPs or tokens. ✅ PRNGs are predictable — use Python'ssecretsmodule for security.
Comparison — true random vs pseudo-random
| True random | Pseudo-random (NumPy) | |
|---|---|---|
| Source | Physical noise (radioactive decay, atmospheric noise) | A mathematical formula |
| Repeatable? | Never | Yes, with the same seed |
| Speed | Slow | Very fast (millions per second) |
| Good for | Cryptography, lotteries | Simulation, sampling, ML, testing |
Interview tip
Q: Why do we set a random seed in machine learning code? A: Train/test splits, weight initialisation and data shuffling all use random numbers. Fixing the seed makes the whole run reproducible, so a change in accuracy can be blamed on a code change rather than on luck.
Key points to remember
- Computers generate pseudo-random numbers: a deterministic formula that only looks random.
- The seed sets the generator's starting state — the same seed always reproduces the same sequence.
- A seed fixes the whole sequence; consecutive calls still give different numbers.
- Without a seed, NumPy takes a fresh seed from the operating system, so each run differs.
- Set the seed once at the top of a script, never inside a loop.
- The value of the seed (42, 0, 2024) has no special meaning — it is just a label for one sequence.
- Never use np.random for security; use Python's secrets module instead.
Try it yourself
Seed, draw, re-seed
No seed vs seed (run it twice!)
Practice: reproducible scores
Revision Flashcards
Tap a card to flip it and see the answer.
