Build Your Own Random Number Generator: A Complete Tutorial Random numbers are the invisible backbone of modern technology. They secure your online bank accounts, ensure fair play in video games, and drive complex data science models. While most programming languages provide built-in random functions, building your own random number generator (RNG) from scratch offers a profound look into computer logic.
This tutorial will guide you through building a mathematical Pseudorandom Number Generator (PRNG) using Python, explaining the theory and code required to create it. Understanding Randomness: PRNG vs. TRNG
Computers are fundamentally deterministic machines. They follow instructions precisely, which makes creating true randomness a unique challenge. Because of this, computer scientists categorize RNGs into two distinct types:
True Random Number Generators (TRNGs): These capture unpredictable physical phenomena from the real world, such as atmospheric noise, thermal jitter, or radioactive decay. They are truly unpredictable but slow to harvest.
Pseudorandom Number Generators (PRNGs): These use mathematical formulas to generate long sequences of numbers that only appear random. They start with an initial value called a seed. If you know the seed and the formula, you can predict the entire sequence. However, they are incredibly fast and efficient.
For everyday programming and gaming, a PRNG is exactly what we need. The Core Algorithm: Linear Congruential Generator
To build our generator, we will use one of the oldest and most famous PRNG algorithms: the Linear Congruential Generator (LCG). First defined by Derrick Henry Lehmer in 1951, the LCG shifts and scales a seed value using basic modular arithmetic. The mathematical formula is straightforward:
Xn+1=(a⋅Xn+c)(modm)cap X sub n plus 1 end-sub equals open paren a center dot cap X sub n plus c close paren space open paren mod space m close paren is the sequence of pseudo-random numbers. X0cap X sub 0 is the seed (the starting value). is the multiplier. is the increment. is the modulus (the maximum range boundary).
The quality of an LCG depends entirely on the choice of these parameters. Poorly chosen numbers will result in a sequence that repeats itself almost immediately. We will use time-tested parameters famously utilized in the numerical recipes of glibc (GNU C Library). Step-by-Step Code Implementation
Let us implement this algorithm cleanly using Python. We will wrap our generator inside a class to manage the state of our seed seamlessly.
import time class LinearCongruentialGenerator: def init(self, seed=None): # LCG Parameters (glibc standards) self.modulus = 231 self.multiplier = 1103515245 self.increment = 12345 # If no seed is provided, use the current system time in milliseconds if seed is None: self.state = int(time.time() * 1000) % self.modulus else: self.state = seed % self.modulus def next_raw(self): “”“Generates the next raw integer state.”“” self.state = (self.multiplier * self.state + self.increment) % self.modulus return self.state def random_float(self): “”“Returns a random float between 0.0 (inclusive) and 1.0 (exclusive).”“” return self.next_raw() / self.modulus def random_int(self, min_val, max_val): “”“Returns a random integer between min_val and max_val (inclusive).”“” # Map the float range to our desired integer range range_size = max_val - min_val + 1 return min_val + int(self.random_float() * range_size) Use code with caution. Testing Your Implementation
Now that our class is established, let us initialize the generator and see it in action.
# Instantiate the generator without a seed (uses current time) rng = LinearCongruentialGenerator() print(“— Generating Floats between 0.0 and 1.0 —”) for _ in range(5): print(f”{rng.random_float():.6f}“) print(” — Simulating Dice Rolls (1 to 6) —“) for _ in range(5): print(rng.random_int(1, 6)) Use code with caution. The Power of the Seed
One major benefit of PRNGs is reproducibility. If you pass the exact same seed to two different instances, they will output the exact same list of numbers. This is incredibly useful for debugging games or replicating scientific simulations.
# Create two generators with the exact same seed rng_a = LinearCongruentialGenerator(seed=42) rng_b = LinearCongruentialGenerator(seed=42) print(” — Testing Reproducibility —“) print(f”Generator A: {rng_a.random_int(1, 100)}“) print(f”Generator B: {rng_b.random_int(1, 100)}“) # This will match Generator A perfectly Use code with caution. Limitations and Next Steps
Congratulations! You have built a functional random number generator from scratch. However, before using this custom script in production environments, you must keep two limitations in mind:
Not Cryptographically Secure: LCGs are highly predictable if an attacker captures a small sample of outputs. Never use this for password generation, token creation, or cryptography. For secure needs, algorithms like Mersenne Twister (Python’s default random) or cryptographically secure libraries (Python’s secrets module) are required.
Periodicity: Eventually, all mathematical PRNGs repeat their sequence. The glibc parameters used here have a period of 2312 to the 31st power
, which is fine for simple scripts but insufficient for massive data simulations.
Building an LCG demystifies how computers fake unpredictability. From this foundation, you can explore advanced randomness concepts like shuffling algorithms, perlin noise for terrain generation, or Monte Carlo simulations. To continue expanding your program, please
See how to run statistical tests to prove its distribution fairness.
Learn how to optimize the script for cryptographic security.
Leave a Reply