How to generate random numbers in Python
Learn to generate random numbers in Python. This guide covers methods, tips, real-world uses, and how to debug common errors.

Python provides simple ways to generate random numbers for simulations, cryptography, and games. Its built-in random module offers powerful, flexible tools for this purpose.
In this article, you'll learn various techniques to generate random numbers. We'll cover practical tips, real-world applications, and advice for common bug fixes to help you master Python's randomness capabilities.
Basic random numbers with random.random()
import random
random_number = random.random()
print(random_number)--OUTPUT--0.7237845954
The random.random() function is the core of the module, generating a pseudo-random floating-point number. This number will always fall within a specific range—greater than or equal to 0.0 and strictly less than 1.0.
Think of this function as the fundamental building block. Many other functions in the random module use its output internally, scaling it to produce integers, select items from a list, or generate numbers from different statistical distributions.
Basic random number techniques
While random.random() provides the base, you'll often use more specific functions to generate integers, create floats in a range, or select random elements.
Generating integers with random.randint()
import random
random_integer = random.randint(1, 100)
print(random_integer)--OUTPUT--42
For generating a random whole number, random.randint() is your go-to function. It accepts two arguments that define the start and end of your desired integer range.
- The most important thing to remember is that this range is inclusive. A call like
random.randint(1, 100)can return any integer from 1 to 100, including both endpoints.
This behavior makes it perfect for common tasks like simulating a dice roll or picking a random ID number.
Creating random floats in a range with random.uniform()
import random
random_float = random.uniform(1.5, 8.5)
print(random_float)--OUTPUT--5.273546789
For generating a random float within a specific range, you'll want to use random.uniform(). It takes two arguments that define the boundaries for your number.
- The key difference from
random.randint()is that it produces a floating-point number. - The range is also inclusive, meaning the result can be equal to either of the endpoints you provide.
This is perfect for tasks that need decimal precision, like simulating measurements or financial data.
Selecting random elements with random.choice()
import random
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
random_fruit = random.choice(fruits)
print(random_fruit)--OUTPUT--banana
When you need to pick a single, random item from a list or another sequence, random.choice() is the perfect tool. It takes a sequence—like the fruits list in the example—and returns one of its elements at random. This is useful for everything from lottery draws to choosing a random player to start a game, or when you need to randomize order by shuffling lists in Python.
- It's important to note that
random.choice()requires a non-empty sequence. If you try to use it on an empty list, Python will raise anIndexError.
Advanced random number techniques
While the built-in random module is great for simple tasks, you'll often need more control for complex applications like scientific computing or machine learning.
Generating arrays of random numbers with NumPy
import numpy as np
random_array = np.random.random(5)
print(random_array)--OUTPUT--[0.12345678 0.23456789 0.34567891 0.45678912 0.56789123]
When you need a whole set of random numbers, NumPy is your best friend. It's a library designed for high-performance numerical computing. Instead of generating numbers one by one, you can create an entire array in a single step. This approach is particularly useful when creating lists of random numbers.
- The
np.random.random(5)function generates a NumPy array containing five random floats between 0.0 and 1.0. - This method is incredibly efficient, especially when you're working with large datasets for tasks like simulations or data analysis.
Creating random numbers with specific distributions
import numpy as np
normal_distribution = np.random.normal(0, 1, 5)
print(normal_distribution)--OUTPUT--[-0.12345678 0.23456789 -1.34567891 0.45678912 1.56789123]
Real-world data isn't always uniform. NumPy excels at generating numbers that follow specific statistical patterns, like the bell curve of a normal distribution. The np.random.normal() function is perfect for this, allowing you to model phenomena like measurement errors or financial market returns.
- The first argument,
0, sets the mean—the center of the distribution. - The second,
1, defines the standard deviation, which controls how spread out the numbers are. - The final argument,
5, specifies how many numbers you want to create.
Setting seeds for reproducible random numbers
import random
import numpy as np
random.seed(42)
np.random.seed(42)
print(random.random())
print(np.random.random())--OUTPUT--0.6394267984578837
0.37454011884736133
While "random" numbers seem unpredictable, they're actually generated by a deterministic algorithm. By setting a seed with random.seed() or np.random.seed(), you give that algorithm a fixed starting point. This makes your random numbers reproducible—a crucial feature for testing and sharing your work.
- When you use the same seed value, like
42, you'll get the exact same sequence of numbers every time you run your script. - This is invaluable for debugging, as it removes randomness from the equation. It also ensures that scientific or machine learning experiments can be replicated by others.
- Notice that both the standard
randommodule andNumPyneed to be seeded separately, as they use their own independent random number generators.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. This lets you move from learning individual techniques to building complete applications much faster.
With Agent 4, you can describe the app you want to build, and it will handle the coding, databases, APIs, and deployment. Instead of just piecing together functions, you can build a working product directly from your description.
- A dice-rolling simulator for tabletop games that uses
random.randint()to generate outcomes. - A "magic 8-ball" utility that pulls a random answer from a list of possible responses with
random.choice(). - A simple financial modeling tool that simulates market changes using numbers from a normal distribution with NumPy.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
Even with simple functions, you can run into a few common issues when generating random numbers in Python.
- Forgetting to set a seed for reproducible results. Without setting a seed using
random.seed(), your code will produce different numbers on each run. This makes debugging tricky and prevents others from replicating your results. - Confusing
random.randint()andrandom.randrange(). These two functions handle their upper bounds differently, which can cause off-by-one errors. Remember thatrandom.randint(a, b)includesbin the possible outcomes, whilerandom.randrange(a, b)excludes it. - Requesting too large a sample. The
random.sample()function is used to choose multiple unique items from a list. If you ask for more items than the list contains—for example, sampling 10 items from a list of five—Python will raise aValueError.
Forgetting to set a seed with random.seed() for reproducible results
When you don't set a seed with random.seed(), your script produces different numbers every time. This unpredictability makes debugging a headache and sharing your work nearly impossible. The following code snippet shows this exact problem in action.
import random
# Different results each time the script runs
result1 = random.randint(1, 100)
# If we run the script again, we can't reproduce the same numbers
print(f"Random number: {result1}")
Because no seed is provided, random.randint() generates a different integer with every run. This makes your results unpredictable and hard to test. The corrected code below shows how to fix this for consistent output.
import random
# Set seed for reproducibility
random.seed(42)
result1 = random.randint(1, 100)
# Same seed will produce the same sequence of random numbers
print(f"Reproducible random number: {result1}")
By calling random.seed(42) before generating a number, you fix the generator's starting point. This ensures the call to random.randint(1, 100) will always produce the same result on every run. It's essential for debugging, as it removes unpredictability. You should always set a seed when you need your results to be consistent for testing or sharing your code with others, especially in scientific computing and machine learning experiments.
Confusion between random.randint() and random.randrange() boundaries
It's easy to get tripped up by the subtle difference between random.randint() and random.randrange(). The key distinction lies in how each function treats its upper limit, which can easily introduce off-by-one errors. The following code demonstrates this behavior clearly.
import random
# Trying to get random number between 1 and 10
value1 = random.randint(1, 10) # Includes 10
value2 = random.randrange(1, 10) # Excludes 10
print(f"Values: {value1}, {value2}")
Because random.randint() includes the endpoint, it can return 10, while random.randrange() stops at 9. This mismatch is a common pitfall that leads to off-by-one errors. See how to align their behavior in the code below.
import random
# Getting random number between 1 and 10
value1 = random.randint(1, 10) # Includes 10
value2 = random.randrange(1, 11) # Now includes 10
print(f"Values: {value1}, {value2}")
To fix the mismatch, you simply increase the upper bound for random.randrange() by one. Now, random.randrange(1, 11) generates a number between 1 and 10, matching the inclusive behavior of random.randint(1, 10). This prevents off-by-one errors. Always double-check your range boundaries when your logic depends on including the final number, especially in simulations or when working with indices where the last element must be a possible outcome.
Error when using random.sample() with sample size larger than population
The random.sample() function pulls a unique set of items from a sequence. A frequent mistake is requesting a sample larger than the sequence itself. This action is impossible and triggers a ValueError. The following code demonstrates this exact scenario.
import random
fruits = ["apple", "banana", "cherry"]
# Trying to get 5 random fruits from a list of 3
random_fruits = random.sample(fruits, 5)
print(random_fruits)
The code requests five unique fruits from a list of only three. Since random.sample() can't create items out of thin air, it raises an error. The fix below shows how to correctly manage the sample size.
import random
fruits = ["apple", "banana", "cherry"]
# Sample safely with min() function
sample_size = min(5, len(fruits))
random_fruits = random.sample(fruits, sample_size)
print(random_fruits)
The solution uses min() to cap the sample size, preventing the ValueError. The code calculates the sample_size by taking the smaller value between your desired amount and the actual length of the list. This ensures random.sample() never gets an impossible request.
You should use this technique whenever the data you're sampling from could be smaller than the sample size you want, which is common when dealing with dynamic or user-provided data.
Real-world applications
Now that you know how to avoid common pitfalls, you can apply these functions to practical problems like password generation and scientific modeling.
Generating random passwords with random.choice()
The random.choice() function is ideal for building a password generator, as it can select random characters from a combined set of letters, digits, and punctuation.
import random
import string
characters = string.ascii_letters + string.digits + string.punctuation
password = ''.join(random.choice(characters) for _ in range(12))
print(password)
This code generates a password by first creating a large character set. It uses the + operator to combine all letters (string.ascii_letters), digits (string.digits), and punctuation (string.punctuation) from Python's string module into a single source string. For more comprehensive approaches to making passwords in Python, you can explore additional security considerations.
- A generator expression then calls
random.choice()12 times. It's a concise way to pick one random character from the source string in each iteration. - Finally, the
''.join()method efficiently stitches these 12 characters together into the final password string. This technique is fundamental for generating random strings of any kind.
Estimating π with Monte Carlo simulation
You can even use randomness to approximate mathematical constants—a Monte Carlo simulation, for instance, can estimate π by generating thousands of random points and checking how many fall inside a circle.
import random
import math
points = 10000
inside_circle = sum(1 for _ in range(points) if random.random()**2 + random.random()**2 <= 1)
pi_estimate = 4 * inside_circle / points
print(f"Estimated π: {pi_estimate}")
print(f"Math.pi: {math.pi}")
This code estimates π by simulating thousands of random points in a square. It’s a classic example of a Monte Carlo method, which uses randomness to solve problems that might be difficult to tackle with exact formulas.
- A generator expression with
sum()efficiently counts how many of the 10,000 points fall inside an inscribed circle. - The condition
random.random()**2 + random.random()**2 <= 1uses the Pythagorean theorem to check if a point is within the circle's boundary. - The final estimate is calculated by multiplying the ratio of points inside the circle by 4.
Get started with Replit
Turn your knowledge into a real tool. Just tell Replit Agent what you want, like "a secure password generator" or "a dice rolling simulator for tabletop games."
It writes the code, tests for errors, and deploys your app directly from your description. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



