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…

I Tried Writing Python Without Importing random — Here’s What Happened Instead
Photo by David Kovalenko on Unsplash

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:

  1. A better appreciation of Python’s random module
  2. 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.

Photo by Alexandru Acea on Unsplash