Python
nCr function
combinatorics
programming
math library

Is there a math nCr function in Python?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Yes. Python 3.8+ has math.comb(n, r) which computes the binomial coefficient nCr (the number of ways to choose r items from n items without order). It returns an exact integer, handles edge cases (r > n returns 0, r = 0 returns 1), and is implemented in C for speed. For older Python versions, use math.factorial(n) // (math.factorial(r) * math.factorial(n - r)) or scipy.special.comb. For permutations (nPr), use math.perm(n, r) (also Python 3.8+).

math.comb (Python 3.8+)

python
1import math
2
3# Basic combinations: C(n, r) = n! / (r! * (n-r)!)
4print(math.comb(5, 2))    # 10 — 5 choose 2
5print(math.comb(10, 3))   # 120
6print(math.comb(52, 5))   # 2598960 — poker hands
7
8# Edge cases
9print(math.comb(5, 0))    # 1 — one way to choose nothing
10print(math.comb(5, 5))    # 1 — one way to choose everything
11print(math.comb(5, 6))    # 0 — can't choose more than available
12print(math.comb(0, 0))    # 1
13
14# Negative values raise ValueError
15# math.comb(-1, 2)  # ValueError: n must be a non-negative integer

math.perm — Permutations (Python 3.8+)

python
1import math
2
3# Permutations: P(n, r) = n! / (n-r)!
4print(math.perm(5, 2))    # 20 — ordered arrangements of 2 from 5
5print(math.perm(10, 3))   # 720
6print(math.perm(5, 5))    # 120 — same as 5!
7
8# Relationship: comb(n, r) = perm(n, r) / factorial(r)
9assert math.comb(10, 3) == math.perm(10, 3) // math.factorial(3)

For Older Python Versions (Pre-3.8)

python
1import math
2
3def comb(n, r):
4    """Compute C(n, r) for Python < 3.8."""
5    if n < 0 or r < 0:
6        raise ValueError("n and r must be non-negative")
7    if r > n:
8        return 0
9    return math.factorial(n) // (math.factorial(r) * math.factorial(n - r))
10
11print(comb(10, 3))  # 120
12
13# More efficient: avoid computing large intermediate factorials
14def comb_efficient(n, r):
15    if r > n:
16        return 0
17    if r > n - r:
18        r = n - r  # Optimization: C(n, r) == C(n, n-r)
19    result = 1
20    for i in range(r):
21        result = result * (n - i) // (i + 1)
22    return result
23
24print(comb_efficient(100, 50))  # 100891344545564193334812497256

Using scipy.special.comb

python
1from scipy.special import comb
2
3# Returns float by default
4print(comb(10, 3))          # 120.0
5print(comb(10, 3, exact=True))  # 120 (exact integer)
6
7# Works with arrays
8import numpy as np
9n_values = np.array([5, 10, 20])
10r_values = np.array([2, 3, 5])
11print(comb(n_values, r_values))  # [10. 120. 15504.]
12
13# With repetition (combinations with replacement)
14print(comb(5, 2, repetition=True))  # 15.0
15# Equivalent to C(n+r-1, r) = C(6, 2) = 15

Practical Applications

python
1import math
2
3# Lottery probability: picking 6 numbers from 49
4total_combos = math.comb(49, 6)
5probability = 1 / total_combos
6print(f"Lottery odds: 1 in {total_combos:,}")  # 1 in 13,983,816
7
8# Pascal's triangle
9def pascals_triangle(rows):
10    for n in range(rows):
11        row = [math.comb(n, r) for r in range(n + 1)]
12        print(' '.join(f'{x:4d}' for x in row).center(rows * 5))
13
14pascals_triangle(6)
15#    1
16#   1  1
17#  1  2  1
18# 1  3  3  1
19# ...
20
21# Binomial probability: P(X=k) = C(n,k) * p^k * (1-p)^(n-k)
22def binomial_probability(n, k, p):
23    return math.comb(n, k) * (p ** k) * ((1 - p) ** (n - k))
24
25# Probability of exactly 3 heads in 10 coin flips
26print(f"{binomial_probability(10, 3, 0.5):.4f}")  # 0.1172
27
28# Counting subsets
29n = 10
30total_subsets = sum(math.comb(n, r) for r in range(n + 1))
31print(f"Total subsets of {n} elements: {total_subsets}")  # 1024 = 2^10

Generating Actual Combinations

python
1from itertools import combinations
2
3# math.comb counts them, itertools.combinations generates them
4items = ['A', 'B', 'C', 'D']
5
6# Count
7print(math.comb(4, 2))  # 6
8
9# Generate
10for combo in combinations(items, 2):
11    print(combo)
12# ('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')
13
14# With replacement
15from itertools import combinations_with_replacement
16for combo in combinations_with_replacement(items, 2):
17    print(combo)
18# ('A', 'A'), ('A', 'B'), ..., ('D', 'D') — 10 total

Common Pitfalls

  • Using math.comb on Python < 3.8: math.comb was added in Python 3.8. On older versions, you get AttributeError: module 'math' has no attribute 'comb'. Use the factorial formula or install scipy as a fallback. Check your version with sys.version_info >= (3, 8).
  • Confusing comb (combinations) with perm (permutations): math.comb(5, 2) = 10 (unordered), math.perm(5, 2) = 20 (ordered). If order matters (arrangements), use perm. If order does not matter (selections), use comb.
  • Integer overflow in manual implementations: The factorial formula n! / (r! * (n-r)!) computes very large intermediate values. For comb(1000, 500), 1000! has thousands of digits. Python handles big integers natively, but iterative multiplication-division (like comb_efficient above) avoids huge intermediates and runs faster.
  • scipy.special.comb returning float by default: Without exact=True, scipy.special.comb returns a float, which loses precision for large values. comb(100, 50) as a float has rounding errors. Always pass exact=True when you need an exact integer result.
  • Negative inputs silently returning wrong results in custom functions: math.comb raises ValueError for negative inputs, but a naive factorial-based implementation may not check. Always validate that n >= 0 and r >= 0 in custom implementations to match the mathematical definition.

Summary

  • Use math.comb(n, r) for combinations (Python 3.8+) — exact integer, C-speed
  • Use math.perm(n, r) for permutations (Python 3.8+)
  • For Python < 3.8, use iterative multiplication-division to avoid large intermediate factorials
  • Use itertools.combinations to generate actual combinations, math.comb to count them
  • For array/batch operations, use scipy.special.comb with exact=True for precision

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.