Categories
General Python level 1 python

A Comprehensive Guide to the Python Random Library

The random library is a built-in Python library with many uses. We’ve covered some ways to use the library such as when creating a Dice Roll Simulator, a Password Generator, or when drawing a card in the card game War. Now that we’ve seen it in action a few times, it would be a good idea to learn more about the library in depth.

The random library in Python generates numbers using the Mersenne Twister method. The Mersenne Twister is a pseudorandom number generator with deterministic results. The implementation is done in C and is threadsafe, which means it can be run in multithreaded code without fear of data contamination. Let’s take a look at the functions of the random library and how we can use them.

In this post we will cover:

Manipulating the State of the Random Library in Python

The Mersenne Twister is a deterministic psuedorandom number generator. It produces the same results each time given the same starting state. There are two ways to manipulate the starting state of the random library in Python. You can use either seed or state.

Python Random Seed

The random.seed() method takes one parameter. That parameter can be of type None, int, float, str, bytes, or bytearray. This is the easiest way to set the state of the random number generator object such that we can generate the same sequence of numbers every time. Let’s see how this works in action with the following Python code.

import random
 
random.seed(22)
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))

Using a seed of 22 and generating 5 random integers from 1 to 5 should give us the same result each time. I got the list 2, 2, 1, 5, 4. When you run the above code, you should also get the exact same list.

Python Random Library Random State

Setting the seed isn’t the only way to manipulate the state of the Python random library. We can also manipulate it with state. There are two functions involved in this. The first function is getstate() and the second function is setstate(). The getstate() function takes 0 parameters and the setstate() function takes 1 parameter of type tuple. You can’t just pass any tuple though, it has to make sense to the function, and the function expects a tuple with the same structure as the one returned in getstate(). Let’s generate two sets of 5 random integers from 1 to 5 with the same state. All we have to do is set x to the returned value from getstate() and pass it to random.setstate() to set the state of the random number generator. If we set the state to x twice, the two sequences should be the same.

import random
 
x = random.getstate()
random.setstate(x)
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
random.setstate(x)
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(random.randint(1, 5))
print(x)

As we can see in the image below, the first 5 and the second 5 numbers are the same.

Python Random State Example

Notice we also printed out x, the tuple representing the state above. Let’s take a look at what that looks like.

Python Random State Tuple Format

As you can see, that’s a lot of numbers. x is a tuple of size 3 where the first entry is an int, the second is a tuple of length 625, and the last is None. The first number represents the version number, the middle tuple represents a list of 624 seed numbers and the last one is always 624, the last number is the Gaussian Next, used to generate numbers from a Gaussian distribution (which we aren’t doing).

Random Seed vs Random State

What’s the difference between using Random Seed and Random State for the random library in Python? The random.seed() function allows us to create universally reproducible results. Using getstate() and setstate() allows us to set the state back to where it was at the beginning of a program. Since the state is taken from the internal state of the computer, this does not guarantee universally reproducible results unless you export the state. However, it is useful for reproducing results within a program. 

Generating Random Integers in Python

Generating random integers has many useful applications such as selecting an index from a list, selecting a number to guess, or drawing the lottery. The Python random library gracefully supplies us with three functions (really two, we’ll go over this) to generate random integers. These functions are random.randrange, random.randint, and random.getrandbits. Let’s take a look at what they do and how they work.

Pick a Random Integer from a Range

The random.randrange() function takes 1 required parameter and 2 optional parameters. If only 1 parameter is passed in, the function generates a number between 0 and the passed in parameter. When two parameters are passed in, the random.range() function generates a number between the two passed in numbers. If three parameters are passed in, the random.range() function generates a number between the two passed in numbers in increments of the third number. All parameters are expected to be integers. 

For example, if we pass in 10, we expect an output between 0 and 10 (not including 10). If we pass in 10 and 20, we expect an output between 10 and 20 (not including 20). If we pass in 10, 20, and 2, we expect an even number greater than or equal to 10 and less than 20.

import random
random.seed(1)
 
print(random.randrange(10))
print(random.randrange(10, 20))
print(random.randrange(10, 20, 2))

The above code (with the random.seed() set to 1)  should generate the numbers 2, 19, and 10 every time. Try it for yourself!

Pick a Random Integer with Python randint

The random.randint() function is actually just a wrapper around the random.randrange() function. The randint function takes two parameters. It returns a number between the two passed in integers. However, unlike the randrange function, the randint function includes the higher integer in its possible return values. You can think of it as a wrapper around random.randrange(a, b+1)

import random
random.seed(1)
 
print(random.randint(10, 20))

With a seed of 1, the above code should always return 10.

Pick a Random Integer of a Certain Size with Python

The random.getrandbits function takes one parameter, the number of bits. It returns a number represented by the number of bits. In other words, it returns a number between 0 and 2 to the x where x is the parameter. For example, if we call random.getrandbits(9) we expect a number between 0 and 512. 

import random
random.seed(1)
 
print(random.getrandbits(9))

With a random seed of 1, random.getrandbits(9) should return 60 every time.

Using the Random Library on Lists in Python

So far we’ve seen how to manipulate the state of the random object and generate random integers in Python. Now let’s take a look at how we can use the random number on iterables in Python. I specifically put lists in the section title, but we can use these on any iterable in Python including strings, lists, tuples, sets, byte sequences, and ranges.

Before we play around with the different functions for manipulating a sequence in Python, let’s establish a sequence to use. To do this, we set a random seed and then generate a sequence of 10 numbers from 1 to 100 using list comprehension.

import random
random.seed(69)
 
seq = [random.randint(1, 100) for _ in range(10)]
print(seq)

This will generate the list [88, 5, 13, 22, 9, 78, 45, 42, 71, 54]. Now let’s look at the four functions for sequences in the Python random library: random.choice, random.choices, random.shuffle, and random.sample.

Pick a Random Choice from a Sequence

The random.choice function is the simplest one. It takes one parameter, a non-empty sequence and will raise an error if the sequence is empty. The function returns a random entry from that sequence. Running random.choice on the sequence above should return 42.

import random
random.seed(69)
 
seq = [random.randint(1, 100) for _ in range(10)]
print(seq)
 
print(random.choice(seq))

Pick Multiple Random Choices from a Sequence

This one has an interesting parameter set up. There is one mandatory parameter, the sequence, and three optional parameters, but two of those optional parameters should not be used together. The three optional parameters are weights, cum_weights, and k. The weights and cum_weights parameters default to None meaning each option is weighted the same, and k, the number of choices returned, defaults to 1. 

The reason that weights and cum_weights shouldn’t be used together is because they represent the same thing – the weighted probability that a number in the sequence will be picked. The weights list should be the same length as the sequence. If weights is passed in, it will be turned into cum_weights, so passing in cum_weights saves us a bit of processing time when calling random.choices. The list of weights indicates the “weight” or probability of picking a number. For example, a weight list of [1, 1, 1] will have a cumulative weight list of [1, 2, 3].

import random
random.seed(69)
 
seq = [random.randint(1, 100) for _ in range(10)]
print(seq)
 
weights = [random.randint(1, 10) for _ in range(10)]
print(weights)
cum_weights = [sum(weights[:i+1]) for i in range(10)]
print(cum_weights)
random.seed(1)
print(random.choices(seq, weights=weights, k=5))
random.seed(1)
print(random.choices(seq, cum_weights=cum_weights, k=5))
print(random.choices(seq, k=5))
print(random.choices(seq))

The above code should result in the output below. Note that setting random.seed before each of the random.choices calls with the weights and cum_weights derived from the weights variable results in the same output. Notice that we can also call random.choices with no weights function. If we call it with no k input, it defaults to returning 1 choice. Notice that unlike random.choice, random.choices returns a list.

Python Multiple Random Choices Example

Randomly Shuffle an Interable

Choosing random entries from an iterable isn’t the only thing we can do with the random library. We can also use it to shuffle up our entries like we did in War, Blackjack, and Texas HoldEm. Starting with the same sequence we’ve been using this whole time, we can see how random.shuffle works. It takes one parameter, the sequence, and shuffles it up. We can actually pass an optional argument that returns a random float, but this is almost always just the random() function. 
It’s important to note that random.shuffle() is an in-place shuffle function. It does not return anything. It simply changes the passed in sequence.

import random
random.seed(69)
 
seq = [random.randint(1, 100) for _ in range(10)]
print(seq)
 
random.shuffle(seq)
print(seq)

When we run the above code, we should see an output exactly like the one below.

Python Random Shuffle Example

Get a Random Sample from a List with the Python Random Library

Finally, we can also use the random library to return a sample from a list. What’s the difference between a random sample and random choices? A random sample is non-replacing. Notice that in our random.choices example, the list had a repeat in it, random.sample will not return repeats unless there are repeats in the sequence itself

The random.sample() function takes 3 parameters, 2 mandatory and 1 optional. The two mandatory parameters are the sequence and the number of entries we’d like sampled. The optional parameter is a count parameter that tells the sampling function how many times each entry in the original sequence can be sampled.

import random
random.seed(69)
 
seq = [random.randint(1, 100) for _ in range(10)]
print(seq)
 
print(random.sample(seq, 5))
seq2 = [2, 3, 4]
random.seed(10)
print(random.sample(seq2, 5, counts=[2, 3, 4]))
seq3 = [2, 2, 3, 3, 3, 4, 4, 4, 4]
random.seed(10)
print(random.sample(seq3, 5))

In the example we will see that sampling from a sequence of [2, 3, 4] with a count equal to [2, 3, 4] is equivalent to sampling from a sequence of [2, 2, 3, 3, 3, 4, 4, 4, 4]. The above code should result in the below output every time. Notice that we have to set the random seed before we do the sample to ensure that it’s the same sample.

Python Random Sequences Example

Generating Numbers from Statistical Distributions in Python

There are 12 built-in statistical distributions that are available to generate from in the random library.

  1. The default random distribution, which generates a floating-point number between 0 and 1.
  2. The uniform random distribution, which takes two parameters, a and b. It generates a random number between a and b.
  3. The triangular distribution, which takes three parameters, a, b, and c. It generates a number in the triangular distribution with base from a to b with a peak at c.
  4. The beta distribution, which takes two parameters, alpha, and beta. Generates a number between 0 and 1 from the beta distribution with parameters alpha and beta.
  5. The exponential distribution, which takes one parameter, lambd. Generates a number from the exponential distribution with mean 1/lambd.
  6. The Gamma distribution, which takes two positive valued parameters, alpha, and beta. Generates a number from the probability density function of Gamma with shape parameter alpha and scale parameter beta.
  7. The Gaussian distribution, which is the normal distribution with a faster, but not threadsafe, implementation. The random.gauss() function takes two parameters, mu and sigma. It returns a number from the normal distribution with mean mu and standard deviation sigma.
  8. The Log Normal distribution, which takes two parameters, mu and sigma. It generates a number from the log normal distribution for a normal distribution with mean mu and standard deviation sigma
  9. The Normal distribution, which takes two parameters, mu and sigma. It generates a number from the normal distribution with mean mu and standard deviation sigma.
  10. Von Mises’ distribution, which takes two parameters, mu and kappa. Von Mises’ distribution is the normal distribution for angles. Generates a value between 0 and 2pi with mean mu and concentration kappa. When kappa is set to 0, this becomes a uniform distribution.
  11. The Pareto distribution, which takes one parameter, alpha. It generates a number from the pareto distribution with shape alpha. For reference the famous 80-20 Pareto distribution comes from an alpha of 1.16.
  12. The Weibull distribution, which takes two parameters, alpha and beta. It generates a number from the Weibull distribution with scale alpha, and shape beta.

Code for Sampling from Statistical Distributions with the Python Random Library

The code below shows how we can sample numbers from each of the distributions above:

import random
 
print(f"Sample from basic random distribution from 0 to 1: {random.random()}")
print(f"Uniform Random Sample from 2 to 4: {random.uniform(2, 4)}")
print(f"Triangle Random Sample from 1 to 10 with a mode of 8: {random.triangular(1, 10, 8)}") #defaults to 0, 1, 0.5
print(f"Sample from the Beta distribution alpha=1, beta=2: {random.betavariate(1, 2)}") #a, b > 0
print(f"Sample from exponential distribution with a mean of 1/9: {random.expovariate(9)}") #parameter != 0
print(f"Sample from Gamma distribution with alpha=1, beta=2: {random.gammavariate(1, 2)}") #a, b > 0
print(f"Sample from Gaussian (Normal) with mean 1 and standard deviation 0.1: {random.gauss(1, 0.1)}") #not-thread-safe but faster version of normalvariate
print(f"Sample from Log Normal Distribution with mean 1 and standard deviaiton 0.1: {random.lognormvariate(1, 0.1)}") #the exponential of the normal around a, b
print(f"Sample from Normal Distribution with mean 1 and standard deviation 0.1: {random.normalvariate(1, 0.1)}") #normal distribution
print(f"Sample from von Mises/circular Normal Distribution with mean ~pi, concentration 0.1: {random.vonmisesvariate(3.14, 0.1)}") #a, b are angle and concentration, b=0 gives uniform random angle
print(f"Sample from Pareto distribution with shape 1: {random.paretovariate(1)}") #a is the shape parameter
print(f"Sample from Weibull distribution with scale 2 and shape 3: {random.weibullvariate(2, 3)}") #a is the scale, b is the shape
Samples of Usage of the Python Random Library

Summary of How to Use the Python Random Library

We looked at four big parts of the Python random library in this post. First we looked at manipulating the “state” of the random library. We can manipulate that via random seed and random state.

Next we looked at how to generate random integers. There are three main ways that the Python random library allows us to generate integers. We can use the randint function, generate a random integer from a range, or generate a random integer of a certain bytesize.

After generating random integers, we looked at working with lists. We looked at how to pick a single random choice and picking multiple random choices from a sequence. Then we looked at how to randomly shuffle an iterable. The last list operation we looked at with the Python random library is how to get a random sample from a list.

The last thing we looked at from the random library was generating numbers from statistical distributions. We also covered the 12 statistical distributions that are included.

Further Reading

I run this site to help you and others like you find cool projects and practice software skills. If this is helpful for you and you enjoy your ad free site, please help fund this site by donating below! If you can’t donate right now, please think of us next time.