The Factorial Function Competition: NumPy, Scipy, Math, and Python

The factorial function n! calculates the number of permutations in a set. Say you want to rank three soccer teams Manchester United, FC Barcelona, and FC Bayern München — how many possible rankings exist? The answer is 3! = 3 x 2 x 1 = 6.

In general, to calculate the factorial n!, you need to multiply all positive integer numbers that are smaller or equal to n. For example, if you have 5 soccer teams, there are 5! = 5 x 4 x 3 x 2 x 1 = 120 different pairings.

There are many different ways to calculate the factorial function in Python easily (see alternatives below).

How to Calculate the Factorial in NumPy?

NumPy’s math module is relatively little known. It contains efficient implementations of basic math function such as the factorial function numpy.math.factorial(n).

Here’s an example of how to calculate the factorial 3! with NumPy:

>>> import numpy as np
>>> np.math.factorial(3)
6

The factorial function in NumPy has only one integer argument n. If the argument is negative or not an integer, Python will raise a value error.

How to Calculate the Factorial in Scipy?

The popular scipy library is a collection of libraries and modules that help you with scientific computing. It’s a powerful collection of functionality—built upon the NumPy library. Thus, it doesn’t surprise that the scipy factorial function scipy.math.factorial() is actually a reference to NumPy’s factorial function numpy.math.factorial(). In fact, if you compare their memory addresses using the keyword is, it turns out that both refer to the same function object:

>>> import scipy, numpy
>>> scipy.math.factorial(3)
6
>>> numpy.math.factorial(3)
6
>>> scipy.math.factorial is numpy.math.factorial
True

So you can use both scipy.math.factorial(3) and numpy.math.factorial(3) to compute the factorial function 3!.

As both functions point to the same object, the performance characteristics are the same — one is not faster than the other one.

How to Calculate the Factorial in Python’s Math Library?

As it turns out, not only NumPy and Scipy come with a packaged “implementation” of the factorial function, but also Python’s powerful math library. Here’s an example of how to use the math.factorial(n) function to compute the factorial n!.

>>> import math
>>> math.factorial(3)
6

The factorial of 3 is 6 — nothing new here.

Let’s check whether this is actually the same implementation as NumPy’s and Scipy’s factorial functions:

>>> import scipy, numpy, math
>>> scipy.math.factorial is math.factorial
True
>>> numpy.math.factorial is math.factorial
True

Ha! Both libraries NumPy and Scipy rely on the same factorial function of the math library. Hence, to save valuable space in your code, use the math factorial function if you have already imported the math library. If not, just use the NumPy or Scipy factorial function aliases.

So up ’till now we’ve seen the same old wine in three different bottles: NumPy, Scipy, and math libraries all refer to the same factorial function implementation.

How to Calculate the Factorial in Python?

It’s often a good idea to implement a function by yourself. This will help you understand the underlying details better and gives you confidence and expertise. So let’s implement the factorial function in Python.

To calculate the number of permutations of a given set of n elements, you use the factorial function n!. The factorial is defined as follows:

n! = n × (n – 1) × ( n – 2) × . . .  × 1

For example:

  • 1! = 1
  • 3! = 3 × 2 × 1 = 6
  • 10! = 10 × 9 × 8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 3,628,800
  • 20! = 20 × 19 × 18 × . . .  × 3 × 2 × 1 = 2,432,902,008,176,640,000

Recursively, the factorial function can also be defined as follows:

n! = n × (n – 1)!

The recursion base cases are defined as shown here:

1! = 0! = 1

The intuition behind these base cases is that a set with one element has one permutation, and a set with zero elements has one permutation (there is one way of assigning zero elements to zero buckets).

Now, we can use this recursive definition to calculate the factorial function in a recursive manner:

>>> factorial = lambda n: n * factorial(n-1) if n > 1 else 1
>>> factorial(3)
6

The lambda keyword is used to define an anonymous function in a single line. You can learn everything you need to know about the lambda function in this comprehensive tutorial on our blog.

You create a lambda function with one argument n and assign the lambda function to the name factorial. Finally, you call the named function factorial(n-1) to calculate the result of the function call factorial(n).

Roughly speaking, you can use the simpler solution for factorial(n-1) to construct the solution of the harder problem factorial(n) by multiplying the former with the input argument n. As soon as you reach the recursion base case n <= 1, you simply return the hard-coded solution factorial(1) = factorial(0) = 1.

An alternative is to use the iterative computation like this:

def factorial(n):
    fac = n
    for i in range(1, n):
        fac *= i
    return fac

print(factorial(3))
# 6

print(factorial(5))
# 120

In the function factorial(n), we initialize the variable fac to the value n. Then, we iterate over all values i between 1 and n-1 (inclusive) and multiply them with the value currently stored in the variable fac. The result is the factorial of the integer value n.

Speed Comparison

Let’s compare all three different ways to calculate the factorial function regarding speed. Note that the NumPy, Scipy, and math factorial functions are referencing to the same function object—they have the same speed properties. Thus, we only compare the math.factorial() function with our two implementations in Python (recursive and iterative).

Want to take a guess first?

I used my own notebook computer (Quadcore, Intel Core i7, 8th Generation) with Python 3.7 to run 900 factorial computations for each method using the following code:

import time

num_runs = 900
speed = []


## SPEED TEST MATH.FACTORIAL ##
import math


start = time.time()
for i in range(num_runs):
    math.factorial(i)
stop = time.time()

speed.append(stop-start)

    
## SPEED TEST RECURSIVE ##
factorial = lambda n: n * factorial(n-1) if n > 1 else 1

start = time.time()
for i in range(num_runs):
    factorial(i)
stop = time.time()

speed.append(stop-start)

    
## SPEED TEST ITERATIVE ##
def factorial(n):
    fac = n
    for i in range(1, n):
        fac *= i
    return fac


start = time.time()
for i in range(num_runs):
    factorial(i)
stop = time.time()

speed.append(stop-start)


## RESULT
print(speed)
# [0.011027336120605469, 0.10074210166931152, 0.0559844970703125]
import matplotlib.pyplot as plt
plt.bar(["Math", "Recursive", "Iterative"], height=speed)
plt.show()

Wow—the clear winner is the math module! A clear sign that you should always prefer library code over your own implementations!

The math library’s implementation is almost 600% faster than the iterative one and 1000% faster than the recursive implementation.

Methodmath.factorialRecursiveIterative
Seconds0.010.100.05

Where to Go From Here

The three library implementations numpy.math.factorial(), scipy.math.factorial(), and math.factorial() point to the same function object in memory—they are identical so use any of them.

One a higher-level, you’ve learned that library implementations of popular libraries such as NumPy are blazingly fast and efficient. Do yourself a favor and use library implementations wherever possible.

A good place to start is the NumPy library which is the basis of many more advanced data science and machine learning libraries in Python such as matplotlib, pandas, tensorflow, and scikit-learn. Learning NumPy will set the foundation on which you can build your Python career.

If you liked this article, you’ll also like my NumPy book “Coffee Break NumPy” that leads you step-by-step into the NumPy library in a fun, engaging, and interactive way. Pro coders read more books!