Randomness is all around us. When you flip a coin or roll a die, you can never be sure of the outcome. This unpredictability has a lot of applications, like determining the winners of a lucky draw or generating test cases for an experiment with random values produced based on an algorithm.
Keeping this usefulness in mind, Python has provided us with the random module. You can use it in games to spawn enemies randomly or to shuffle the elements in a list.
Types of Functions  Example Functions 
Initialize and use the random number generator 
seed() , random()

Random integers in a range 
randrange() , randint()

Random items from a sequence 
choice() , shuffle() , sample()

Random floats with standard distributions 
triangular() , uniform() , normalvariate()

Random items from a weighted list 
choice() , choices() , sample()

How Does Random Work?
Nearly all of the functions in this module depend on the basic random()
function, which will generate a random float greater than or equal to zero and less than one. Python uses the Mersenne Twister to generate the floats. It produces 53bit precision floats with a period of 2^{19937}1. It is actually the most widely used generalpurpose pseudorandom number generator.
Initialize the Random Number Generator With seed()
Sometimes, you want the random number generator to reproduce the sequence of numbers it created the first time. This can be achieved by providing the same seed value both times to the generator using the seed(s, version)
function. If the s
parameter is omitted, the generator will use the current system time to generate the numbers. Here is an example:
import random random.seed(100) random.random() # returns 0.1456692551041303 random.random() # returns 0.45492700451402135
Keep in mind that unlike a coin flip, the module generates pseudorandom numbers which are completely deterministic, so it is not suitable for cryptographic purposes.
Generating Random Integers
Generate Integers in a Range With randrange()
and randint()
The module has two different functions for generating random integers. You can use randrange(a)
to generate a random whole number smaller than a
.
Similarly, you can use randrange(a, b[,step])
to generate a random number from range(a, b, step)
. For example, using random.randrange(0, 100, 3)
will only return those numbers between 0 and 100 which are also divisible by 3.
If you know both the lower and upper limits between which you want to generate the numbers, you can use a simpler and more intuitive function called randint(a, b)
. It is simply an alias for randrange(a, b+1)
.
import random random.randrange(100) # returns 65 random.randrange(100) # returns 98 random.randrange(0, 100, 3) # returns 33 random.randrange(0, 100, 3) # returns 75 random.randint(1,6) # returns 4 random.randint(1,6) # returns 6
Functions for Sequences
Chose a Random Element From a List With choice()
To select a random element from a given nonempty sequence, you can use the choice(seq)
function. With randint()
, you are limited to a selection of numbers from a given range. The choice(seq)
function allows you to choose a number from any sequence you want.
Another good thing about this function is that it is not limited to just numbers. It can select any type of element randomly from a sequence. For example, the name of the winner of a lucky draw among five different people, provided as a string, can be determined using this function easily.
Shuffle a Sequence With shuffle()
If you want to shuffle a sequence instead of selecting a random element from it, you can use the shuffle(seq)
function. This will result in an in place shuffling of the sequence. For a sequence with just 10 elements, there can be a total of 10! = 3,628,800 different arrangements. With a larger sequence, the number of possible permutations will be even higher—this implies that the function can never generate all the permutations of a large sequence.
Sample Multiple Times With sample()
Let's say you have to pick 50 students from a group of 100 students to go on a trip.
At this point, you may be tempted to use the choice(seq)
function. The problem is that you will have to call it about 50 times in the best case scenario where it does not choose the same student again.
A better solution is to use the sample(seq, k)
function. It will return a list of k
unique elements from the given sequence. The original sequence is left unchanged. The elements in the resulting list will be in selection order. If k is greater than the number of elements in the sequence itself, a ValueError
will be raised.
import random ids = [1, 8, 10, 12, 15, 17, 25] random.choice(ids) # returns 8 random.choice(ids) # returns 15 names = ['Tom', 'Harry', 'Andrew', 'Robert'] random.choice(names) # returns Tom random.choice(names) # returns Robert random.shuffle(names) names # returns ['Robert', 'Andrew', 'Tom', 'Harry'] random.sample(names, 2) # returns ['Andrew', 'Robert'] random.sample(names, 2) # returns ['Tom', 'Robert'] names # returns ['Robert', 'Andrew', 'Tom', 'Harry']
As you can see, shuffle(seq)
modified the original list, but sample(seq, k)
kept it intact.
Generating Random Floats With Standard Distributions
In this section, you will learn about functions that can be used to generate random numbers based on specific realvalue distributions. The parameters of most of these functions are named after the corresponding variable in that distribution's actual equation.
When you just want a number between 0 and 1, you can use the random()
function. If you want the number to be in a specific range, you can use the uniform(a, b)
function with a and b as the lower and higher limits respectively.
Generating Random Floats With Probability Distributions
Let's say you need to generate a random number between low
and high
such that it has a higher probability of lying in the vicinity of another number mode
. You can do this with the triangular(low, high, mode)
function. The low
and high
values will be 0 and 1 by default. Similarly, the mode
value defaults to the midpoint of the low and high values, resulting in a symmetrical distribution.
There are a lot of other functions as well to generate random numbers based on different distributions. As an example, you can use normalvariate(mu, sigma)
to generate a random number based on a normal distribution, with mu
as the mean and sigma
as the standard deviation.
Example Random Values From Probability Distributions
import random random.random() # returns 0.8053547502449923 random.random() # returns 0.05966180559620815 random.uniform(1, 20) # returns 11.970525425108205 random.uniform(1, 20) # returns 7.731292430291898 random.triangular(1, 100, 80) # returns 42.328674062298816 random.triangular(1, 100, 80) # returns 73.54693076132074
Random Items With Weighted Probabilities
As we just saw, it is possible to generate random numbers with a uniform distribution as well as a triangular or normal distribution. Even in a finite range like 0 to 100, an infinite number of floats can be generated. What if there is a finite set of elements and you want to add more weight to some specific values while selecting a random number? This situation is common in lottery systems where numbers with little reward are given a high weighting.
Choosing From a Weighted List With choice(seq)
If it is acceptable for your application to have weights that are integer values, you can create a list of elements whose frequency depends on their weight. You can then use the choice(seq)
function to select an element from this weighted list randomly. Here is an example showing the selection of a prize amount randomly.
import random w_prizes = [('$1', 300), ('$2', 50), ('$10', 5), ('$100', 1)] prize_list = [prize for prize, weight in w_prizes for i in range(weight)] random.choice(prize_list) # returns '$1'
In my case, it took ten trials to get a $2 prize chosen from the list. The chances of getting a $100 prize would be much lower.
Choosing From a Weighted List With random.choices()
Python also has a function called random.choices(population, weights=None, *, cum_weights=None, k=1)
that allows you to natively pick values from a weighted distribution instead of implementing something similar on our own, as we just did. It accepts four arguments, but only the first one is required. Just passing a single list of values to the function will give you back one item from the list.
As you can see below, our weighted probability code could easily be rewritten to get a list of values using the random.choices()
function.
import random prizes = ['$1', '$2', '$10', '$100'] weightings = [300, 50, 5, 1] print(random.choices(prizes, weightings, k=10)) # ['$1', '$1', '$1', '$1', '$2', '$1', '$1', '$1', '$1', '$2'] print(random.choices(prizes, k=10)) # ['$1', '$1', '$1', '$10', '$10', '$2', '$100', '$10', '$2', '$2']
Values are selected with equal probability if you don't provide weightings. The choices()
function will repeat some of the returned values in the final selected sample. You should note that this is different from the sample()
function we discussed earlier, which returns a list of unique values from the given length. Passing a value of k
higher than the population length will result in a ValueError
with sample()
but works with choices()
. Here is an example:
import random prizes = ['$1', '$2', '$10', '$100'] print(random.choices(prizes, k=10)) # ['$100', '$1', '$1', '$10', '$10', '$100', '$10', '$1', '$10', '$2'] print(random.sample(prizes, k=10)) # ValueError: Sample larger than population or is negative
The choices()
function is useful for simulating things like a coin toss or a dice throw because there is a possibility of repetition. On the other hand, sample()
is useful for things like picking people randomly for different teams as the same person cannot be picked for two teams.
The sample()
function was updated in version 3.9 to accept an additional counts
parameter, which is simply a list that specifies how many times specific values are repeated in a population. You can use this parameter to simulate weighted distribution.
import random fruits = ['apple', 'mango', 'banana', 'guava'] numbers = [50, 30, 12, 100] print(random.sample(fruits, 10, counts=numbers)) # ['guava', 'apple', 'apple', 'apple', 'guava', 'guava', 'mango', 'apple', 'apple', 'guava']
This is useful in situations where you have to pick something randomly (e.g. fruits from a basket) and then distribute them. Using sample()
means that there is no possibility of selecting more bananas than the total amount present in the basket. The counts
parameter allows us to avoid creating an actual list of 50 apples, 100 guavas, etc.
Keeping all these subtle differences between the functions in mind will help you write code that doesn't show unexpected behavior.
Final Thoughts
This module can be useful in a lot of situations, like shuffling the questions in an assignment or generating random usernames or passwords for your users by using the shuffle()
function. You can also generate random numbers uniformly, as well as giving weighting to numbers in a specific range. In our next tutorial, we will be using the functions from this module to generate random data for statistical analysis.
Do you have some interesting applications of random number generators in mind that can be useful to fellow readers? Let us know on the forum.