Combinatorial Arithmetic in Python: Exact Factorials with math.factorial()

The math.factorial(n) function computes the product of all positive integers less than or equal to non-negative integer n (denoted mathematically as n!), where 0! = 1. Implemented internally in optimized C, it leverages Python's arbitrary-precision integer architecture to compute factorials of arbitrarily large integers without integer overflow.

Combinatorics Foundation: Factorials form the computational backbone for permutations (math.perm(n, k)) and binomial coefficient combinations (math.comb(n, k)), widely applied in statistics, machine learning, and cryptography.

Signatures, Parameters & Return Types 🔝

The math.factorial() function takes a non-negative integer and returns its exact factorial:

import math

result = math.factorial(n)
Parameter Type Description
n int Required. A non-negative integer (n ≥ 0). In Python 3.9+, float values raise ValueError even if integral (e.g. 5.0).
Return Value int Returns exact integer n! with unlimited precision.
ValueError Exception Raised when n < 0 or when n is a float.
TypeError Exception Raised when n is not an integer-like object.

Basic Usage & Large Number Computation 🔝

In languages like C or Java, standard 64-bit integers overflow at 21!. In Python, integers grow dynamically to consume available memory, allowing calculations of astronomical quantities:

import math

# Small factorials
print("0! =", math.factorial(0))    # 1
print("1! =", math.factorial(1))    # 1
print("5! =", math.factorial(5))    # 120
print("10! =", math.factorial(10))  # 3628800

# Large factorial: 50!
fact_50 = math.factorial(50)
print("\n50! =", fact_50)
print("Number of digits in 50!:", len(str(fact_50)))

# 100! (158 decimal digits)
fact_100 = math.factorial(100)
print("\nNumber of digits in 100!:", len(str(fact_100)))
print("100! begins with:", str(fact_100)[:20], "...")

Output:

0! = 1
1! = 1
5! = 120
10! = 3628800

50! = 30414093201713378043612608166064768844377641568960512000000000000
Number of digits in 50!: 65

Number of digits in 100!: 158
100! begins with: 93326215443944152681 ...

Implementation Comparison: math.factorial vs. Loop vs. Recursion 🔝

While factorial can be written iteratively or recursively in Python, math.factorial() is significantly faster because it is implemented in C and uses divide-and-conquer binary splitting algorithms:

import math
import sys

# 1. Iterative implementation
def factorial_iterative(n):
    res = 1
    for i in range(2, n + 1):
        res *= i
    return res

# 2. Recursive implementation
def factorial_recursive(n):
    if n <= 1:
        return 1
    return n * factorial_recursive(n - 1)

# Verification
n = 20
print("math.factorial:     ", math.factorial(n))
print("factorial_iterative:", factorial_iterative(n))
print("factorial_recursive:", factorial_recursive(n))

# Recursion trap: exceeding maximum recursion depth
try:
    factorial_recursive(1500)
except RecursionError as e:
    print("\nRecursion trap caught:", e)

Output:

math.factorial:      2432902008176640000
factorial_iterative: 2432902008176640000
factorial_recursive: 2432902008176640000

Recursion trap caught: maximum recursion depth exceeded in comparison
Why Avoid Recursion for Factorials in Python: Standard Python has a recursion limit of 1,000 frames (sys.getrecursionlimit()). Calling factorial_recursive(1500) triggers a RecursionError. Always use math.factorial() for production systems.

Input Validation & Boundary Error Handling 🔝

Understanding how math.factorial() handles invalid arguments prevents unexpected runtime crashes:

import math

# 1. Negative numbers raise ValueError
try:
    math.factorial(-5)
except ValueError as e:
    print("Negative error:", e)

# 2. Float values raise ValueError (Python 3.9+)
try:
    math.factorial(5.0)
except ValueError as e:
    print("Float error:   ", e)

# 3. Non-numeric types raise TypeError
try:
    math.factorial("10")
except TypeError as e:
    print("String error:  ", e)

Output:

Negative error: factorial() not defined for negative values
Float error:    factorial() only accepts integral values
String error:   'str' object cannot be interpreted as an integer

Permutations & Combinations (math.perm and math.comb) 🔝

Factorials are the mathematical foundation for counting arrangements (order matters) and selections (order does not matter):

Concept Formula Standard Library Function Description
Permutations P(n, k) = n! / (n - k)! math.perm(n, k) Ways to choose and arrange k items from n distinct items.
Combinations C(n, k) = n! / (k! · (n - k)!) math.comb(n, k) Ways to choose k items from n without regard to order.
import math

n, k = 10, 3

# Manual factorial formulas
perm_manual = math.factorial(n) // math.factorial(n - k)
comb_manual = math.factorial(n) // (math.factorial(k) * math.factorial(n - k))

# Built-in math functions
perm_native = math.perm(n, k)
comb_native = math.comb(n, k)

print(f"P(10, 3) = {perm_native} (manual: {perm_manual})")
print(f"C(10, 3) = {comb_native}  (manual: {comb_manual})")

Output:

P(10, 3) = 720 (manual: 720)
C(10, 3) = 120  (manual: 120)

Real-World Applications: Binomial Probability & Anagrams 🔝

1. Binomial Distribution (Quality Control Defect Probability)

The probability of getting exactly k successes in n independent Bernoulli trials with probability p is P(X = k) = C(n, k) · pk · (1 - p)n - k:

import math

def binomial_probability(trials, successes, prob_success):
    comb = math.comb(trials, successes)
    return comb * (prob_success ** successes) * ((1.0 - prob_success) ** (trials - successes))

# Probability of exactly 2 defective parts in a batch of 50 (defect rate = 3%)
prob_2_defects = binomial_probability(trials=50, successes=2, prob_success=0.03)
print(f"Probability of 2 defects in batch of 50: {prob_2_defects:.4%}")

Output:

Probability of 2 defects in batch of 50: 25.5508%

2. Distinct Anagram Permutations with Repeated Characters

The number of distinct arrangements of a word with duplicate letters is Total! / (count_1! · count_2! · …):

import math
from collections import Counter

def distinct_permutations(word):
    counts = Counter(word.upper())
    total_length = len(word)
    denominator = 1
    for count in counts.values():
        denominator *= math.factorial(count)
    return math.factorial(total_length) // denominator

print("Distinct anagrams of 'BOOK':       ", distinct_permutations("BOOK"))
print("Distinct anagrams of 'MISSISSIPPI':", distinct_permutations("MISSISSIPPI"))

Output:

Distinct anagrams of 'BOOK':        12
Distinct anagrams of 'MISSISSIPPI': 34650

Hands-on Exercise: Lottery Odds & Multinomial Permutations 🔝

The following production script calculates lottery jackpot probabilities and evaluates combinatorial anagrams with automated self-checking assertions:

Python Exercise: Combinatorial Odds & Permutation Engine
import math
from collections import Counter

def lottery_jackpot_odds(total_pool, pick_count, bonus_pool=1):
    # Calculates 1 in X odds of winning jackpot
    if pick_count > total_pool or total_pool <= 0:
        raise ValueError("Invalid pool or pick count.")
    
    main_combinations = math.comb(total_pool, pick_count)
    total_tickets = main_combinations * bonus_pool
    return total_tickets

def multinomial_arrangements(items_list):
    # Number of unique sequences of items with duplicates
    counts = Counter(items_list)
    n = len(items_list)
    denom = 1
    for c in counts.values():
        denom *= math.factorial(c)
    return math.factorial(n) // denom

# --- Unit Assertions and Verification ---

# 1. 0! identity and small values
assert math.factorial(0) == 1
assert math.factorial(1) == 1
assert math.factorial(6) == 720

# 2. Standard 6/49 Lottery Odds: C(49, 6) = 13,983,816
odds_6_49 = lottery_jackpot_odds(total_pool=49, pick_count=6)
assert odds_6_49 == 13_983_816

# 3. Powerball Odds: C(69, 5) * 26 = 292,201,338
powerball_odds = lottery_jackpot_odds(total_pool=69, pick_count=5, bonus_pool=26)
assert powerball_odds == 292_201_338

# 4. Anagram permutation tests
assert multinomial_arrangements(list("BOOK")) == 12
assert multinomial_arrangements(list("PEPPER")) == 60  # 6! / (3! * 2!) = 720 / 12 = 60

print("Powerball jackpot odds: 1 in", f"{powerball_odds:,}")
print("Unique anagrams of 'PEPPER':", multinomial_arrangements(list("PEPPER")))
print("All combinatorial unit assertions passed successfully!")

Practice in Google Colab & GitHub 🔝

Open in Google Colab View on GitHub

Run all factorial calculations interactively, evaluate massive 100! integers, benchmark recursive vs. iterative algorithms, and test lottery combinations in Google Colab.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer