I Tried Writing Python Without Importing random — Here’s What Happened Instead
Ditching Python’s random module might sound crazy, but it taught me a lot about how randomness actually works—and how to hack it with pure…

No import random
? Challenge accepted.
I Tried Writing Python Without Importing random
— Here’s What Happened Instead
Ditching Python’s random
module might sound crazy, but it taught me a lot about how randomness actually works—and how to hack it with pure Python.
We reach for import random
without even thinking. Need to pick a number, shuffle a list, simulate a dice roll? It’s always the go-to.
But one day, I asked myself:
What if I couldn’t use it?
Not for a coding interview or some weird restriction — just out of curiosity. Could I replicate its core functionality? Could I simulate randomness without the convenience of Python’s random
module?
So I tried it. And it turned into a fascinating deep dive into entropy, timestamps, and Python’s flexibility.
No random
, No Problem?
Let’s get one thing straight: True randomness doesn’t exist in pure software. What we get from random
is actually pseudo-randomness—numbers generated in a way that appears random, but is ultimately deterministic.
So if random
is just a fancy deterministic function, maybe I could write my own.
Here’s how it went.
Step 1: Use What You’ve Got — Time
My first instinct? Use the current time as a seed. Here’s a quick hack to generate something that feels random:
import time
def my_randint(start, end):
timestamp = time.time()
fractional = timestamp - int(timestamp)
scaled = int(fractional * (end - start + 1))
return start + scaled
Explanation:
time.time()
gives the current time in seconds (with decimals).
The decimal part changes fast — perfect for our “random” seed.
Scale it to the desired range.
Calling my_randint(1, 6)
gives different results if you run it at different moments.
Step 2: Roll My Own Pseudo-RNG
To get fancier, I built a simple linear congruential generator (LCG) — a classic technique for pseudo-random number generation.
class LCG:
def __init__(self, seed=None):
self.modulus = 2**32
self.a = 1664525
self.c = 1013904223
self.state = seed or int(time.time())
def rand(self):
self.state = (self.a * self.state + self.c) % self.modulus
return self.state
def randint(self, low, high):
return low + self.rand() % (high - low + 1)
Now I had my own randint
—just like random.randint
—but no random
module needed.
Step 3: Shuffle Without random.shuffle
Shuffling a list is tricky without built-in randomness. I wrote my own version of the Fisher-Yates shuffle:
def my_shuffle(lst, rng):
for i in range(len(lst) - 1, 0, -1):
j = rng.randint(0, i)
lst[i], lst[j] = lst[j], lst[i]
Use it like this:
rng = LCG()
my_list = [1, 2, 3, 4, 5]
my_shuffle(my_list, rng)
It works! I had my own basic version of shuffling.
So… Why Bother?
Honestly, I didn’t do this to reinvent the wheel. I did it to understand the wheel.
Here’s what I learned:
random
hides a lot of complexity—but it’s all reproducible if you know the math.
Custom random generators help you understand seeding, determinism, and entropy.
In security contexts, you shouldn’t roll your own randomness. Use secrets
or cryptographic libraries instead.
But for learning? Writing your own randomness logic is deeply enlightening.
Final Thoughts
At the end of this experiment, I gained two things:
- A better appreciation of Python’s
random
module - The confidence that I don’t need it in a pinch
If you’re someone who likes poking under the hood of your tools, I highly recommend trying this.
Not because it’s practical — but because it’s fun.
What about you?
Ever tried to rewrite a standard library just to understand how it works? Share your war stories in the comments.
