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
cf = continued_fraction(Rational(10, 7))
print(cf)
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.
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
)
cf_list = [1, 2, 3]
fraction = continued_fraction_reduce(cf_list)
print(f"[1, 2, 3] -> {fraction}")
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/11The 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
)
cf_iterator = continued_fraction_iterator(pi)
convergents = continued_fraction_convergents(cf_iterator)
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)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
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
)
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
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()
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.7182818285Function 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.