Python SymPy: How to Work with Continued Fractions in SymPy in Python
Continued fractions represent numbers through nested divisions, revealing optimal rational approximations and exposing hidden mathematical structures. For irrational numbers like pi or the square root of 2, continued fractions provide the "best" fractional approximations with the smallest possible denominators. Python's SymPy library offers a complete toolkit for converting between representations, computing convergents, and analyzing periodic patterns in square roots.
This guide covers every major function in SymPy's continued fraction module, from basic conversions to finding the best rational approximation for any real number.
Converting Rational Numbers to Continued Fractions
The continued_fraction() function converts a rational number into its continued fraction representation, returned as a list of integers:
from sympy import Rational
from sympy.ntheory.continued_fraction import continued_fraction
# Convert 10/7 to its continued fraction
# 10/7 = 1 + 1/(2 + 1/3), which gives [1, 2, 3]
cf = continued_fraction(Rational(10, 7))
print(cf)
# Another example: 45/16
cf2 = continued_fraction(Rational(45, 16))
print(cf2)
Output:
[1, 2, 3]
[2, 1, 4, 3]
Each integer in the list represents a level of the nested fraction. The first element is the whole number part, and the remaining elements describe the successive reciprocals.
Never pass raw Python division like 10/7 directly. Python evaluates this as a float before SymPy ever sees it, introducing rounding errors that corrupt the result:
# Wrong: Python evaluates 10/7 as 1.4285714285714286 (a float)
continued_fraction(10/7) # May produce an incorrect, very long list
# Correct: Rational preserves the exact fraction
continued_fraction(Rational(10, 7)) # [1, 2, 3]
Always use Rational(numerator, denominator) to maintain exact precision.
Converting Back to Fractions
Use continued_fraction_reduce() to convert a continued fraction list back into a rational number:
from sympy import Rational
from sympy.ntheory.continued_fraction import (
continued_fraction,
continued_fraction_reduce
)
# Reconstruct a fraction from a continued fraction list
cf_list = [1, 2, 3]
fraction = continued_fraction_reduce(cf_list)
print(f"[1, 2, 3] -> {fraction}")
# Verify round-trip conversion
original = Rational(355, 113)
cf = continued_fraction(original)
reconstructed = continued_fraction_reduce(cf)
print(f"{original} -> {list(cf)} -> {reconstructed}")
Output:
[1, 2, 3] -> 10/7
355/113 -> [3, 7, 16] -> 355/11
The round-trip conversion is exact. Any rational number can be converted to a continued fraction and back without any loss of precision.
Finding Convergents for Irrational Numbers
Convergents are the successive rational approximations that result from truncating a continued fraction at each step. They approach the true value more and more closely. For pi, these convergents reveal famous fractions like 22/7 and 355/113:
from sympy import pi
from sympy.ntheory.continued_fraction import (
continued_fraction_iterator,
continued_fraction_convergents
)
# Create an iterator for pi's infinite continued fraction
cf_iterator = continued_fraction_iterator(pi)
# Wrap it in a convergent generator
convergents = continued_fraction_convergents(cf_iterator)
# Extract the first 6 approximations
approximations = [next(convergents) for _ in range(6)]
for approx in approximations:
error = abs(float(approx) - float(pi))
print(f" {str(approx):>10} = {float(approx):.10f} (error: {error:.2e})")
Output:
3 = 3.0000000000 (error: 1.42e-01)
22/7 = 3.1428571429 (error: 1.26e-03)
333/106 = 3.1415094340 (error: 8.32e-05)
355/113 = 3.1415929204 (error: 2.67e-07)
103993/33102 = 3.1415926530 (error: 5.78e-10)
104348/33215 = 3.1415926539 (error: 3.32e-10)
The fraction 355/113 is remarkably accurate. It matches pi to six decimal places despite having small, memorable digits. This makes it one of the most famous rational approximations in mathematics, and its quality is directly visible in the continued fraction representation.
Periodic Continued Fractions for Square Roots
Square roots of non-perfect-square integers always produce periodic (repeating) continued fractions. SymPy can identify this repeating pattern using continued_fraction_periodic():
from sympy.ntheory.continued_fraction import continued_fraction_periodic
# Find the continued fraction for several square roots
# Parameters: continued_fraction_periodic(a, b, c) represents (a + b*sqrt(c)) / 1
# For simple sqrt(n), use (0, 1, n)
roots = [2, 3, 5, 7, 13]
for n in roots:
result = continued_fraction_periodic(0, 1, n)
print(f" sqrt({n:>2}) = {result}")
Output:
sqrt( 2) = [1, [2]]
sqrt( 3) = [1, [1, 2]]
sqrt( 5) = [2, [4]]
sqrt( 7) = [2, [1, 1, 1, 4]]
sqrt(13) = [3, [1, 1, 1, 1, 6]]
The first element is the integer part. The list inside the brackets is the repeating portion. For example, the square root of 2 has a continued fraction of [1, 2, 2, 2, 2, ...] where the 2 repeats forever.
The function parameters represent the form (a + b * sqrt(c)). For a simple square root of n, use continued_fraction_periodic(0, 1, n).
Generating Approximations for Square Roots
You can combine the continued fraction iterator with the convergent generator to produce rational approximations for any square root:
from sympy import sqrt
from sympy.ntheory.continued_fraction import (
continued_fraction_iterator,
continued_fraction_convergents
)
# Get rational approximations for sqrt(2)
sqrt2_iter = continued_fraction_iterator(sqrt(2))
sqrt2_convergents = continued_fraction_convergents(sqrt2_iter)
actual = float(sqrt(2))
print("Rational approximations for sqrt(2):")
for i, approx in enumerate(sqrt2_convergents):
if i >= 8:
break
error = abs(float(approx) - actual)
print(f" {str(approx):>12} = {float(approx):.10f} (error: {error:.2e})")
Output:
Rational approximations for sqrt(2):
1 = 1.0000000000 (error: 4.14e-01)
3/2 = 1.5000000000 (error: 8.58e-02)
7/5 = 1.4000000000 (error: 1.42e-02)
17/12 = 1.4166666667 (error: 2.45e-03)
41/29 = 1.4137931034 (error: 4.20e-04)
99/70 = 1.4142857143 (error: 7.22e-05)
239/169 = 1.4142011834 (error: 1.24e-05)
577/408 = 1.4142156863 (error: 2.12e-06)
Each convergent is the best possible rational approximation with a denominator of that size or smaller.
Practical Application: Best Rational Approximation with a Denominator Limit
A common practical problem is finding the best rational approximation to a value where the denominator cannot exceed a certain limit. Convergents make this straightforward:
from sympy import pi, E, sqrt
from sympy.ntheory.continued_fraction import (
continued_fraction_iterator,
continued_fraction_convergents
)
def best_approximation(value, max_denominator):
"""Find the best rational approximation within a denominator limit."""
cf_iter = continued_fraction_iterator(value)
convergents = continued_fraction_convergents(cf_iter)
best = None
for approx in convergents:
if approx.q > max_denominator:
break
best = approx
return best
# Best approximation to pi with denominator at most 1000
approx_pi = best_approximation(pi, 1000)
print(f"Best pi approximation (denom <= 1000): {approx_pi}")
print(f" Value: {float(approx_pi):.10f}")
print(f" Actual: {float(pi):.10f}")
print()
# Best approximation to e with denominator at most 100
approx_e = best_approximation(E, 100)
print(f"Best e approximation (denom <= 100): {approx_e}")
print(f" Value: {float(approx_e):.10f}")
print(f" Actual: {float(E):.10f}")
Output:
Best pi approximation (denom <= 1000): 355/113
Value: 3.1415929204
Actual: 3.1415926536
Best e approximation (denom <= 100): 193/71
Value: 2.7183098592
Actual: 2.7182818285
Function Reference
| Function | Purpose | Example |
|---|---|---|
continued_fraction() | Convert a rational to a CF list | continued_fraction(Rational(22, 7)) |
continued_fraction_reduce() | Convert a CF list back to a rational | continued_fraction_reduce([3, 7, 15]) |
continued_fraction_iterator() | Create an iterator for infinite CFs | continued_fraction_iterator(pi) |
continued_fraction_convergents() | Generate successive approximations | Wraps an iterator from above |
continued_fraction_periodic() | Find the periodic CF for square roots | continued_fraction_periodic(0, 1, 5) |
Summary
SymPy's continued fraction module provides exact symbolic tools for working with one of the most powerful representations in number theory.
- Use
continued_fraction()andcontinued_fraction_reduce()for exact round-trip conversion of rational numbers. - Use
continued_fraction_iterator()paired withcontinued_fraction_convergents()to generate successively better rational approximations of any real number, including irrational constants like pi and e. - Use
continued_fraction_periodic()to discover the repeating patterns in the continued fractions of square roots. - Always use
Rational()instead of Python float division to prevent rounding errors from corrupting your results.