faqts : Computers : Programming : Languages : Python : Snippets : Maths

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

11 of 16 people (69%) answered Yes
Recently 6 of 10 people (60%) answered Yes

Entry

module for continued fractions, useful for finding optimal fractional approximation to real numbers

Jul 11th, 2000 04:19
unknown unknown, Huaiyu Zhu


#!/usr/bin/env python
# (C) 2000 Huaiyu Zhu <hzhu@users.sourceforge.net>.      Licence: GPL
# $Id: ContFrac.py,v 1.3 2021/07/10 05:09:11 hzhu Exp $
"""
ContFrac.py - Continued fraction approximation utilities
        Convert between float, continued fraction and fractions.
        Useful for finding optimal rational approximation at any 
precision.
Usage examples: python ContFrac.py
Representation:
        Continued fraction: list of integers.
        Ordinary fraction: pair of long integers. 
Optional variables in the functions:
        nterm: max number of terms in continued fraction.
        prec:  precition (inverse relative error) - maybe too 
conservative?
"""
def cf2real(cf):
        "Convert to float from continued fraction"
        x = 0.
        for i in range(1,len(cf)+1):            x = 1./(cf[-i] + x)
        return  1./x
def real2cf(x, prec=1e16, nterms=40):
        "Convert to continued fraction from float"
        cf = [];        p = 1.; a = 1
        for i in range(nterms):
                a, a0 = int(x), long(a)
                cf.append(a)
                p = p * (abs(a*a0) + 1)
                if p > prec or abs(x-a)<1e-12: break
                x = 1./(x-a)
        "Merge last term if it is one, so the expression is unique"
        n = len(cf)-1
        if cf[n] == 1: cf[n-1]=cf[n-1]+1; del cf[n]
        return cf
def cf2frc(cf, prec=1e16):
        "Convert to fraction from continued fraction"
        "Estimates term n that achieves precision - need better 
algorithm"
        p = 1.; n = 0
        for n in range(1, len(cf)):
                p = p * (cf[n-1]*(cf[n]+.5)+1)
                if p >= prec: break
        "Conversion using first n terms"
        x, y = long(cf[n]), 1L
        for i in range(n):
                x, y = x*cf[n-1-i] + y, x
        return  x, y
def frc2cf(frc):
        "Convert to continued fraction from fraction"
        cf = []
        x, y = frc
        while 1:
                if y == 0: break
                a, b  = x/y, x%y
                cf.append(a)
                x, y = y, b
        return cf
#------------------------------------------------------------------
def test(x, prec, nterms):
        print "Convert %.16f to continued fraction and back to float" % 
x
        cf = real2cf(x, nterms=nterms)
        print cf,  cf2real(cf)
        print "Convert to fraction and back to float, limit precision: 
%g" %prec
        print "num. of terms                     fraction       float   
        error"
        for n in range(1, nterms+1):
                f = cf2frc(cf[:n], prec)
                r = f[0]/float(f[1])
                print "%2d      %33s  % .16f  %- 8.4g" % (n, f, r, 
r/x-1)
if __name__ == "__main__":
        from math import pi, exp, sqrt
        print "------------ Effect of too much precision and terms 
------------"
        test(-0.6667, prec=1e19, nterms=6)
        print "------------ Effect of limited number of terms 
----------------"
        test(pi, prec=1e16, nterms=6)
        print "------------ Effect of limited of precision 
--------------------"
        test(exp(1), prec=1e4, nterms=9)
        # Interesting pattern in continued fraction of e.
        print "------------ Conversion between fraction and continued 
fraction"
        cf = real2cf(1/sqrt(2), nterms=17); print cf
        f = cf2frc(cf); print f
        cf1 = frc2cf(f); print cf1
        if cf == cf1: print "Idential"
        else: print "Something wrong"