How to create a list of random numbers in Python

Learn how to create a list of random numbers in Python. Explore different methods, tips, real-world applications, and common error fixes.

How to create a list of random numbers in Python
Published on: 
Tue
Mar 17, 2026
Updated on: 
Tue
Mar 24, 2026
The Replit Team

Python makes it easy to create lists of random numbers for simulations, data science, and gaming. Its built-in modules offer simple and powerful tools for this purpose.

In this article, you'll learn several techniques to create these lists. You'll also get practical tips, see real-world applications, and receive debugging advice to master random number generation.

Using random.randint() with list comprehension

import random
random_numbers = [random.randint(1, 100) for _ in range(10)]
print(random_numbers)--OUTPUT--[42, 27, 93, 11, 65, 87, 32, 18, 59, 74]

This approach uses a list comprehension, which is a compact way to build a list. It works by:

  • Looping ten times using for _ in range(10).
  • Calling random.randint(1, 100) on each loop to get a random integer.
  • Adding each new integer directly into the list being created.

This method is both Pythonic and efficient, letting you generate the entire list in a single, readable line of code.

Basic random number generation techniques

While list comprehensions are a great shortcut, it's also helpful to understand the more traditional methods and specialized functions in Python's random module.

Using a for loop to generate random numbers

import random
random_numbers = []
for _ in range(10):
random_numbers.append(random.randint(1, 100))
print(random_numbers)--OUTPUT--[37, 82, 19, 56, 45, 91, 23, 68, 12, 77]

This classic for loop approach is more explicit than a list comprehension, breaking the process into clear, sequential steps. While slightly more verbose, it clearly shows how the list is built one element at a time.

  • First, you initialize an empty list with [].
  • The loop then iterates a set number of times.
  • On each pass, random.randint() generates a number, and the append() method adds it to your list.

Using random.sample() for unique random numbers

import random
random_numbers = random.sample(range(1, 101), 10)
print(random_numbers)--OUTPUT--[27, 83, 41, 95, 12, 67, 38, 54, 9, 76]

When you need a list of random numbers without any duplicates, random.sample() is the perfect tool. It works by pulling a specified number of unique items from a larger sequence, ensuring no repeats.

  • The first argument, range(1, 101), defines the population to draw from—in this case, all integers from 1 to 100.
  • The second argument, 10, tells the function exactly how many unique numbers to select from that population.

This is ideal for scenarios where you can't have duplicates, like drawing lottery numbers or creating a unique set of test data.

Using random.choices() for weighted random selection

import random
weights = [1, 2, 3, 4, 5] * 20
random_numbers = random.choices(range(1, 101), weights=weights, k=10)
print(random_numbers)--OUTPUT--[85, 72, 93, 65, 81, 98, 73, 89, 77, 60]

Sometimes you need random numbers that aren't evenly distributed. The random.choices() function is perfect for this, allowing you to assign different probabilities to each number. This is called weighted random selection, and it allows duplicates in the final list.

  • The weights parameter is a list where each weight corresponds to a number in the population. A higher weight means a higher chance of being picked.
  • The k argument simply tells the function how many numbers you want to generate in total.

Advanced random number techniques

Building on those fundamentals, you can gain more power by using NumPy for performance, generating floating-point numbers, and making your random sequences reproducible.

Using NumPy for efficient random number generation

import numpy as np
random_numbers = np.random.randint(1, 101, size=10).tolist()
print(random_numbers)--OUTPUT--[53, 18, 92, 37, 64, 81, 25, 70, 46, 89]

For generating large lists of random numbers, the NumPy library offers a significant performance boost. It's a powerful package for numerical computing that's much faster than standard Python for these kinds of tasks.

  • The function np.random.randint(1, 101, size=10) generates an array of 10 integers from 1 to 100.
  • Because NumPy creates its own array type, you'll use the .tolist() method to convert it back into a standard Python list.

This method is ideal when working with large datasets where efficiency is key.

Generating random floating-point numbers

import random
random_floats = [random.uniform(0, 1) for _ in range(10)]
print(random_floats)--OUTPUT--[0.23, 0.67, 0.41, 0.95, 0.12, 0.88, 0.54, 0.36, 0.79, 0.05]

Generating random decimals, or floating-point numbers, is just as straightforward. The key is the random.uniform() function, which you can use inside a list comprehension to quickly create a list of floats.

  • This function returns a random floating-point number between two endpoints you provide.
  • For example, random.uniform(0, 1) will produce a value anywhere between 0 and 1, inclusive. This is perfect for simulations or generating normalized data points.

Using seeds for reproducible random numbers

import random
random.seed(42)
random_numbers = [random.randint(1, 100) for _ in range(10)]
print(random_numbers)--OUTPUT--[24, 62, 93, 57, 14, 72, 37, 86, 51, 29]

The numbers computers generate aren't truly random—they're pseudorandom, created by a deterministic algorithm. The random.seed() function gives this algorithm its starting point. By providing a specific seed, like 42 in the example, you guarantee that the exact same sequence of "random" numbers will be produced every time the code runs.

  • This makes your results reproducible, which is invaluable for debugging or testing.
  • You can use any integer for the seed; the important part is that using the same seed will always yield the same output.

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.

For the random number generation techniques we've explored, Replit Agent can turn them into production-ready tools.

  • Build a lottery number generator that uses random.sample() to draw unique winning numbers.
  • Create a game's loot box simulator where item drops are determined by weighted probabilities with random.choices().
  • Deploy a data simulation tool that generates large datasets for performance testing using NumPy's efficient functions.

Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser.

Common errors and challenges

Even with Python's simple tools, a few common pitfalls can trip you up when generating random numbers.

  • Incorrect seed placement for random.seed()
  • A classic mistake is calling random.seed() inside a loop. This resets the random number generator with every iteration, causing it to produce the same number repeatedly instead of a random sequence. To ensure reproducibility for your entire list, you should call random.seed() just once at the beginning of your script.
  • Misunderstanding randint() range parameters
  • It's easy to get the range wrong with random.randint(a, b). Unlike many Python functions where the upper bound is exclusive, randint() is inclusive, meaning the results can include both a and b. This misunderstanding often leads to off-by-one errors, where your generated numbers don't fall within the exact range you intended.
  • Forgetting to handle random module's thread safety
  • Python's built-in random module is not thread-safe, which can cause issues in multi-threaded applications. Because concurrent threads share the same generator state, their calls can interfere with one another, leading to unpredictable results. The solution is to create a separate random.Random() instance for each thread, guaranteeing their random sequences remain independent.

Incorrect seed placement for random.seed()

A classic mistake is placing random.seed() inside a function that you call repeatedly. This resets the random number generator with every call, forcing it to produce the same sequence of numbers instead of a new one. The code below demonstrates this common pitfall.

def generate_numbers():
random.seed(42) # Seed inside function
return [random.randint(1, 100) for _ in range(5)]

import random
first_set = generate_numbers()
second_set = generate_numbers()
print("First set:", first_set)
print("Second set:", second_set) # These will be identical!

Because random.seed(42) is inside the generate_numbers() function, the generator resets every time the function is called. This forces the sequence to restart, making both lists identical. See the corrected approach in the code below.

import random

random.seed(42) # Seed once at the beginning

def generate_numbers():
return [random.randint(1, 100) for _ in range(5)]

first_set = generate_numbers()
second_set = generate_numbers()
print("First set:", first_set)
print("Second set:", second_set) # Now different

By moving random.seed(42) outside the function, you initialize the generator just once at the start of your script. This allows the generate_numbers() function to pull from a continuous sequence of pseudorandom numbers.

The first call gets one set, and the second call gets the next set in the sequence, ensuring they are different. This is crucial for testing scenarios where you need reproducible yet distinct sets of random data across multiple function calls.

Misunderstanding randint() range parameters

It's a classic off-by-one error. Many Python functions exclude the upper bound of a range, but random.randint() is inclusive. Forgetting this means your generated numbers might never reach the maximum value you intended. The following code demonstrates this common mistake.

import random
# Trying to get numbers 1-100 but mistakenly excluding 100
numbers = [random.randint(1, 99) for _ in range(5)]
print(numbers) # Will never contain 100

By setting the upper bound to 99, the code guarantees the number 100 will never be generated. This happens because random.randint() treats both its start and end arguments as possible results. The corrected approach is shown below.

import random
# Both bounds are inclusive in randint
numbers = [random.randint(1, 100) for _ in range(5)]
print(numbers) # Can contain 100

The corrected code works because random.randint(1, 100) is inclusive, meaning it can return both 1 and 100. This is easy to forget if you're used to range(), which excludes its endpoint. Keep an eye on this whenever you use randint() to avoid subtle off-by-one errors. A quick check of your arguments ensures your numbers always cover the full range you actually need.

Forgetting to handle random module's thread safety

Python's default random module isn't thread-safe, which can cause unpredictable behavior in multi-threaded applications. When concurrent threads call functions like random.randint(), they can interfere with the shared generator state, corrupting the output. The following code demonstrates this issue.

import random
import threading

def generate_random_numbers():
return [random.randint(1, 100) for _ in range(5)]

threads = [threading.Thread(target=generate_random_numbers) for _ in range(5)]
for t in threads:
t.start()

The threads all call generate_random_numbers concurrently, creating a race condition for the shared generator state. This can lead to corrupted or non-random output. The following snippet shows how to resolve this issue.

import random
import threading
import time

local_random = random.Random() # Create thread-local instance

def generate_random_numbers():
return [local_random.randint(1, 100) for _ in range(5)]

threads = [threading.Thread(target=generate_random_numbers) for _ in range(5)]
for t in threads:
t.start()

The fix is to create a separate generator instance using random.Random(). This gives each thread its own isolated random number generator, so they no longer share and corrupt a single state. This approach prevents race conditions and ensures each thread produces a clean, independent sequence. It's essential for any multi-threaded application that relies on random data, such as web servers handling concurrent requests or parallel data processing tasks.

Real-world applications

Beyond the syntax and error handling, these random number functions power everything from simple games to advanced scientific models.

Simulating a dice game with random.randint()

The random.randint() function is a natural fit for simulating a dice game, where each roll is just a random integer between 1 and 6.

import random

def roll_dice(num_dice=2):
return [random.randint(1, 6) for _ in range(num_dice)]

dice_results = roll_dice()
print(f"You rolled: {dice_results}, sum: {sum(dice_results)}")

This code defines a flexible roll_dice function that's built around a list comprehension. This compact structure repeatedly calls random.randint(1, 6) to generate a list of outcomes, simulating multiple dice rolls at once.

  • The function’s num_dice parameter controls how many rolls are generated, defaulting to two if you don't provide a number.
  • Finally, the script calls the function, captures the list of rolls, and prints both the individual results and their total sum using an f-string.

Using random.uniform() for Monte Carlo π estimation

The random.uniform() function is perfect for running a Monte Carlo simulation, a method that can approximate π by generating thousands of random points and checking their position relative to a circle.

import random

def estimate_pi(num_points=1000):
points_in_circle = 0
for _ in range(num_points):
x, y = random.uniform(-1, 1), random.uniform(-1, 1)
if x**2 + y**2 <= 1:
points_in_circle += 1
return 4 * points_in_circle / num_points

pi_estimate = estimate_pi(10000)
print(f"π estimate: {pi_estimate} (actual π: 3.14159...)")

This function estimates π by comparing the area of a circle to the square that contains it. It generates thousands of random (x, y) coordinates, with each point falling somewhere inside a 2x2 square centered at the origin.

  • The condition x**2 + y**2 <= 1 uses the equation for a circle to determine if a random point lands inside the unit circle inscribed within that square.
  • The final calculation, 4 * points_in_circle / num_points, approximates π based on the ratio of points that landed inside the circle versus the total generated.

Get started with Replit

Put these techniques into practice with Replit Agent. Describe a tool like, “Build a web app that generates unique lottery numbers,” or “Create a dice roll simulator for tabletop games,” and watch it come to life.

The agent writes the code, tests for errors, and deploys your app. Start building with Replit and bring your ideas from concept to a live application in minutes.

Get started free

Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.