How to Use math.comb and math.perm in Python

Python provides built-in tools for combinatorial calculations through the math.comb() and math.perm() functions introduced in Python 3.8. This article explains how these functions calculate combinations and permutations, their syntax and mathematical formulas, their handling of edge cases, and the underlying performance optimizations that make them superior to manual implementations using factorials.

Permutations with math.perm()

A permutation represents the number of ways to choose and arrange \(k\) items from a set of \(n\) distinct items where the order of selection matters.

The mathematical formula for a permutation is:

\[P(n, k) = \frac{n!}{(n - k)!}\]

In Python, the syntax is:

import math

math.perm(n, k=None)

The function takes two arguments:

Example Usage

import math

# Number of ways to award 1st, 2nd, and 3rd place among 10 competitors
podium_finishes = math.perm(10, 3)
print(podium_finishes)  # Output: 720

# Full arrangement of 5 items (equivalent to math.factorial(5))
all_arrangements = math.perm(5)
print(all_arrangements)  # Output: 120

If \(k > n\), math.perm(n, k) returns 0 because it is impossible to choose more items than are available. If either \(n\) or \(k\) is negative or not an integer, a ValueError or TypeError is raised.


Combinations with math.comb()

A combination represents the number of ways to choose \(k\) items from a set of \(n\) items where the order of selection does not matter.

The mathematical formula for a combination is:

\[C(n, k) = \binom{n}{k} = \frac{n!}{k!(n - k)!}\]

In Python, the syntax is:

import math

math.comb(n, k)

Both arguments \(n\) and \(k\) are required and must be non-negative integers.

Example Usage

import math

# Number of ways to choose a 4-person committee from a group of 12
committee = math.comb(12, 4)
print(committee)  # Output: 495

# Choosing 0 items from 10 always yields 1 way
empty_set = math.comb(10, 0)
print(empty_set)  # Output: 1

Similar to math.perm(), if \(k > n\), math.comb(n, k) evaluates to 0. Passing negative values or non-integers raises a ValueError or TypeError.


How They Compute Combinatorics Internally

Naively implementing permutations or combinations using math.factorial() is computationally inefficient. Computing full factorials such as \(n!\) produces extremely large intermediate numbers, consuming significant memory and processor time before division cancels out the common terms.

Instead, Python’s C implementation uses optimized arithmetic algorithms:

  1. Direct Multiplicative Cancellation: For permutations, math.perm(n, k) computes only the falling factorial:

    \[n \times (n - 1) \times \dots \times (n - k + 1)\]

    This performs exactly \(k\) multiplications instead of computing the full \(n!\) and \((n - k)!\).

  2. Symmetry Optimization: For combinations, \(\binom{n}{k}\) is mathematically identical to \(\binom{n}{n - k}\). If \(k > n / 2\), math.comb() automatically sets \(k = n - k\). This minimizes the number of multiplication and division steps.

  3. C-Level Big Integer Handling: Both functions are implemented directly in C within CPython. They use intermediate reductions to keep number sizes manageable throughout evaluation, returning an exact Python integer regardless of magnitude without risk of floating-point overflow.