How to find the factorial of a number in Python
Discover multiple ways to calculate a number's factorial in Python. Get tips, see real-world applications, and learn to debug common errors.

To find a number's factorial is a fundamental operation in mathematics and programming. Python offers several methods for this task, from simple loops to the built-in math.factorial() function.
In this guide, you'll explore these techniques with practical examples. You will also learn about real-world applications, performance tips, and how to debug common errors in your calculations.
Basic iterative approach with a loop
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
print(factorial(5))--OUTPUT--120
The factorial() function uses a for loop to build the result incrementally. It initializes a result variable to 1, which is crucial because it's the multiplicative identity. This provides a clean starting point for the calculation, ensuring the first multiplication is accurate.
The loop iterates from 1 up to the input number n. In each step, the result *= i operation updates the total by multiplying it with the current number. This process effectively computes the factorial by multiplying each integer in the sequence.
Standard factorial methods
Building on the iterative method, you can also tackle factorials using Python's built-in math library, recursion, or functional programming with functools.reduce().
Using the built-in math.factorial() function
import math
print(math.factorial(5))
print(math.factorial(10))--OUTPUT--120
3628800
Python's math module provides a straightforward function, math.factorial(), for this exact purpose. It's the standard, recommended approach because it’s implemented in C, making it highly efficient and reliable. You just need to import the math library to use it.
- It's generally faster than writing your own loop or recursive function.
- The function handles validation by raising a
ValueErrorif you pass it a negative number. - It only accepts integers, so you'll get a
TypeErrorif you try using a float.
Implementing factorial with recursion
def factorial_recursive(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial_recursive(n - 1)
print(factorial_recursive(5))--OUTPUT--120
Recursion offers an elegant, if less performant, alternative. The factorial_recursive function works by calling itself with a smaller number until it reaches a base condition. This creates a chain of multiplication that unwinds once the base case is met. Understanding recursion in Python is essential for mastering this technique.
- The base case,
if n == 0 or n == 1, is crucial. It returns1and stops the function from calling itself infinitely. - The recursive step,
n * factorial_recursive(n - 1), breaks the problem down into smaller pieces until the base case is reached.
Using functools.reduce() for factorial calculation
from functools import reduce
import operator
def factorial_reduce(n):
return reduce(operator.mul, range(1, n + 1), 1)
print(factorial_reduce(6))--OUTPUT--720
For a more functional approach, you can use functools.reduce(). This function cumulatively applies an operation to a sequence, boiling it down to a single result. It’s a concise way to express complex accumulations.
- The function takes
operator.mul, which tells it to multiply items together. - It applies this multiplication across the
range(1, n + 1). - The final argument,
1, is the initializer—it sets the starting value for the calculation.
Advanced factorial techniques
With the fundamentals covered, you can now explore more advanced techniques like memoization for optimization, sequence generators, and concise lambda function implementations.
Optimizing with memoization
factorial_cache = {}
def factorial_memo(n):
if n in factorial_cache:
return factorial_cache[n]
if n == 0 or n == 1:
return 1
factorial_cache[n] = n * factorial_memo(n - 1)
return factorial_cache[n]
print(factorial_memo(7))--OUTPUT--5040
Memoization is an optimization technique that speeds up functions by caching their results. The factorial_memo function uses a dictionary, factorial_cache, to store values it has already computed, avoiding redundant work in recursive calls.
- Before calculating, the function checks if the result for
nis already in the cache. - If a value is found, it's returned instantly.
- If not, the function computes the factorial, stores the new result in the cache, and then returns it. This makes future calls for the same number significantly faster.
Creating a factorial sequence generator
def factorial_generator(n):
result = 1
for i in range(1, n + 1):
result *= i
yield result
for i, fact in enumerate(factorial_generator(5), 1):
print(f"{i}! = {fact}")--OUTPUT--1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
The factorial_generator function uses the yield keyword to create a generator. Instead of calculating all factorials and returning a list, it produces each value in the sequence one at a time. This approach is highly memory-efficient, especially for large ranges.
- The
yield resultstatement pauses the function, returns the current factorial, and saves its state. When the generator is called again, it resumes right where it left off.
Implementing factorial as a lambda function
factorial_lambda = lambda n: 1 if n <= 1 else n * factorial_lambda(n - 1)
print(factorial_lambda(4))
print(factorial_lambda(8))--OUTPUT--24
40320
A lambda function offers a compact way to define a single-expression function without a formal def statement. This example, factorial_lambda, packs a recursive factorial calculation into one line using a ternary operator for its logic. For more details on using lambda in Python, explore the various applications of these anonymous functions.
- The expression
1 if n <= 1serves as the base case, which stops the recursion. - The recursive step,
else n * factorial_lambda(n - 1), calls the function itself with a decremented value.
While it’s a concise alternative, this approach can be less readable than a standard function, especially for those unfamiliar with recursive lambdas.
Move faster with Replit
Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. Instead of piecing together techniques, you can use Agent 4 to build complete applications. Describe the app you want to build, and the Agent will handle the code, databases, APIs, and deployment.
- A statistical analysis tool that uses factorial calculations for probability modeling.
- A performance dashboard that compares the speed of recursive versus iterative functions with memoization.
- A data sequence utility that uses a generator to process large datasets without consuming excess memory.
Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.
Common errors and challenges
When calculating factorials, you'll need to handle negative inputs, recursion limits, and potential memory issues, but each has a straightforward solution.
Handling negative inputs in factorial() functions
Factorials are mathematically defined only for non-negative integers, so passing a negative number should raise an error. While Python’s built-in math.factorial() automatically raises a ValueError for negative inputs, your custom functions won't do this by default. It's good practice to add a check at the beginning of your function to validate the input and raise an error if n < 0, making your code more robust and predictable.
Avoiding stack overflow in recursive factorial() implementation
Recursive functions can be elegant, but they come with a risk of stack overflow. Each time a function calls itself, it adds a new layer to the call stack. Python has a recursion depth limit to prevent infinite loops from consuming all available memory, so calling a recursive factorial function with a large number can trigger a RecursionError. For calculations involving large numbers, iterative methods or the highly optimized math.factorial() are safer and more efficient choices.
Preventing memory leaks in memoized factorial() functions
Memoization speeds up calculations by caching results, but this can lead to high memory consumption if not managed carefully. In our example, the factorial_cache dictionary grows indefinitely, storing every result it computes. While this isn't a problem for a few calls, in a long-running application or a function called with many different large numbers, the cache can consume a significant amount of memory. To prevent this, you could implement a more advanced caching strategy, such as a fixed-size cache that discards the least recently used items.
Handling negative inputs in factorial() functions
Factorials are only defined for non-negative integers, but a basic iterative function won't handle negative inputs correctly by default. Instead of raising an error, it can return a misleading result. Our simple factorial() function, for instance, returns 1. See what happens in the code below.
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# This will create an empty range and return 1, which is incorrect
print(factorial(-5))
The range(1, n + 1) function doesn't work as expected with a negative n. The loop is skipped entirely, causing the function to return its initial value. See how to handle this case in the code below.
def factorial(n):
if n < 0:
raise ValueError("Factorial is not defined for negative numbers")
result = 1
for i in range(1, n + 1):
result *= i
return result
try:
print(factorial(-5))
except ValueError as e:
print(f"Error: {e}")
To fix this, you can add a simple check at the start of your function. By adding an if n < 0: condition, you can immediately raise ValueError("Factorial is not defined for negative numbers"). This approach makes your function's behavior clear and prevents it from returning a misleading 1. It's good practice to validate inputs this way, especially when a function's mathematical definition has specific constraints, ensuring your code is robust and predictable.
Avoiding stack overflow in recursive factorial() implementation
Recursive functions are elegant, but they aren't built for large numbers. Each call to factorial_recursive() adds a new frame to Python's call stack. When the stack gets too deep, you'll hit the recursion limit, triggering a RecursionError. See it happen below.
def factorial_recursive(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial_recursive(n - 1)
# This will cause a RecursionError for large inputs
print(factorial_recursive(1000))
The call factorial_recursive(1000) creates a chain of nearly 1,000 nested function calls. This exceeds Python's default recursion depth, triggering a RecursionError. While one solution is to increase recursion limit in Python, the code below shows how to restructure the logic to avoid this.
def factorial_iterative(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# Using iteration instead of recursion for large numbers
print(factorial_iterative(1000))
The iterative solution, factorial_iterative, sidesteps the recursion limit entirely. Instead of creating a deep call stack, it uses a simple for loop to multiply numbers one by one. This approach keeps memory usage flat and avoids the RecursionError that plagues recursive functions with large inputs. It's the go-to method when you need to calculate factorials for numbers that would otherwise cause a stack overflow, ensuring your code remains stable and efficient.
Preventing memory leaks in memoized factorial() functions
Memoization is a powerful optimization, but its cache can grow indefinitely and consume significant memory. The factorial_memo function stores every result it computes, which becomes a problem in long-running applications. See how this unbounded growth happens in the code below.
factorial_cache = {}
def factorial_memo(n):
if n in factorial_cache:
return factorial_cache[n]
if n == 0 or n == 1:
return 1
factorial_cache[n] = n * factorial_memo(n - 1)
return factorial_cache[n]
for i in range(1000):
factorial_memo(i)
The for loop populates the factorial_cache with a thousand different results, causing it to grow without bounds and consume excess memory. The following example demonstrates a more controlled approach to caching.
from functools import lru_cache
@lru_cache(maxsize=128)
def factorial_memo(n):
if n == 0 or n == 1:
return 1
return n * factorial_memo(n - 1)
for i in range(1000):
factorial_memo(i)
The @lru_cache decorator from Python's functools library offers a clean fix. It automatically handles memoization, so you don't need to manage a cache dictionary yourself.
- The key is the
maxsizeargument, which limits how many results are stored. - Once the cache is full, it discards the least recently used items to make room for new ones. This prevents unbounded memory growth in long-running applications.
Real-world applications
With those common challenges addressed, you can apply factorials to practical problems in probability and mathematical approximations.
Using factorial() to calculate combinations for lottery odds
The factorial() function is fundamental to calculating combinations, a common task in probability that lets you figure out the odds of events like winning the lottery. Once you have the odds, you can convert them to probabilities by calculating percentages in Python.
def combinations(n, r):
return factorial(n) // (factorial(r) * factorial(n - r))
# Calculate the odds of winning a lottery (choosing 6 numbers from 49)
lottery_odds = combinations(49, 6)
print(f"Odds of winning the lottery (6 from 49): 1 in {lottery_odds}")
This code implements the standard formula for combinations, which calculates how many ways you can choose a subset of items from a larger set. The combinations() function uses the factorial() function you've already seen to determine the odds.
- The parameters
nandrrepresent the total number of items and the number you want to choose, respectively. - It uses integer division
//because the result of a combination calculation is always a whole number. - The example calculates lottery odds by finding all possible combinations of 6 numbers from 49.
Approximating e^x using Taylor series with factorials
Beyond probability, factorials are a key component of the Taylor series, a powerful mathematical tool for approximating the value of functions like e^x.
def exp_approximation(x, terms=10):
result = 0
for n in range(terms):
result += x**n / factorial(n)
return result
# Compare our approximation with math.exp
import math
x = 2
print(f"Our approximation: {exp_approximation(x)}")
print(f"Math.exp result: {math.exp(x)}")
The exp_approximation function builds a sum by adding several calculated values together. The number of values it sums is controlled by the terms parameter—more terms lead to a more accurate result. The function starts with a result of 0 and adds to it in each loop.
- Each value added is the result of the expression
x**n / factorial(n). - The loop runs from
n = 0up to the number ofterms, accumulating the total. - This method uses factorials as part of a series to approximate a complex mathematical value.
Get started with Replit
Turn your new skills into a real tool with Replit Agent. Try prompts like, “Build a probability calculator for combinations,” or “Create a tool to compare factorial function performance.”
The Agent writes the code, tests for errors, and deploys your app automatically. Start building with Replit.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.
Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.



