// Generate continued fraction representation for a number, or turn a // continued fraction back into a number. This also finds the closest fraction // to a number in the most efficient way. // See the book _Continued Fractions_ by A. Ya. Khinchin // Most of the notes and references to theorems and algorithms are taken // from this book. // // See http://mathworld.wolfram.com/ContinuedFraction.html // Generate a continued fraction (as an array) for the number x. // No more than "limit" terms will be produced. // // As part of this process, we build up the represented value (the // "convergent") as we go to determine if we've exceeded the available // working precision. continuedFraction[x, limit=10] := { r = x result = new array count = 0 a = floor[r] var p var q p2 = 1 q2 = 0 p1 = a q1 = 1 while (r != 0) and count < limit { result.push[a] denom = r - a if denom == 0 break r = 1 / denom if count > 0 { p = a p1 + p2 q = a q1 + q2 // println["$p / $q"] if q != 0 and p/q - x == 0 // Error is zero; we're done break p2 = p1 q2 = q1 p1 = p q1 = q } a = floor[r] count = count + 1 } return result } // Turns an array indicating a continued fraction into a single // fraction (or array of fractions) indicating its value. // This performs the calculation top-down. // // Since the result for a canonical continued fraction will always be an // exact rational number, it is safe to do the calculation top-down or // bottom-up. // // if asArray is true, this will return the results as an array of successive // approximations to the value (also called convergents of the continued // fraction.) continuedFractionToFraction[a, asArray=false] := { // n is the order of the continued fraction (the index of the last element, // starting with 0) n = length[a] - 1 if n == 0 return a@0 if asArray == true results = new array // The final value will be p/q. For canonical continued fractions, p and q // will both always be integers. // // An interesting note is that p/q is *already* always in simplest form, // that is, the fraction will never need reducing. Or, in other terms, // gcd[p,q] = 1, or in even other terms, p and q share no common factors. var p var q // p1 and q1 are p and q from the k-1 th (i.e. the previous) step. p1 = a@0 q1 = 1 // p2 and q2 are p(k-2) and q(k-2) (i.e. two steps ago.) // These values are necessary to make the convergent work for k=1 // See Khinchin p.5 (remark) p2 = 1 q2 = 0 if asArray == true results.push[p1] for k = 1 to n { // See Khinchin Theorem 1 p = a@k p1 + p2 q = a@k q1 + q2 // Move current values to previous values p2 = p1 q2 = q1 p1 = p q1 = q if asArray == true results.push[p/q] } if asArray return results else return p/q } /** This is a "bottom-up" calculation of a continued fraction, turning it into a fraction. It's more obvious than the above "top-down" calculation, but this approach doesn't work with infinite continued fractions, and doesn't show the convergence of continued fractions. (All canonical integer-valued continued fractions will converge, see Khinchin, theorem 10) Since for canonical continued fractions the result is an exact rational number, calculations can be done "top-down" or "bottom-up" without loss of accuracy. This version is preserved for obviousness and clarity. **/ /* continuedFractionToFraction[a] := { n = length[a] - 1 frac = 0 for i = n to 1 step -1 frac = 1/(a@i+frac) return a@0 + frac } */ // Returns the closest fraction to x as a fraction by creating a continued // fraction with no more than "limit" terms. closestFraction[x, limit=10] := { cf = continuedFraction[x,limit] return continuedFractionToFraction[cf] } // Turns an array representing a continued fraction back into an array // of fractions. Each fraction in the array shows the results of using more // and more terms from the continued fraction representation. continuedFractionToArray[cf] := { continuedFractionToFraction[cf, true] } // Return an array which is a series of fractions representing // successively-better approximations to the number x. No more than "limit" // terms will be produced. approximations[x, limit=10] := continuedFractionToArray[continuedFraction[x,limit]] // Return an array which is a series of fractions representing // successively-better approximations to the number x. No more than "limit" // terms will be produced. approximationsWithErrors[x, limit=10] := { result = new array approximations = continuedFractionToArray[continuedFraction[x,limit]] for a = approximations result.push[ [a, (a-x)/x] ] return result }