Post Reply 
PDQ Algorithm: Infinite precision best fraction within tolerance
12-13-2013, 05:09 AM (This post was last modified: 10-21-2017 12:36 AM by Joe Horn.)
Post: #1
PDQ Algorithm: Infinite precision best fraction within tolerance
PDQ Algorithm for HP Prime, by Joe Horn

PDQ finds best rational approximations, with infinite precision. This means it finds the two smallest integers whose ratio is equal to some target real number plus or minus some desired tolerance. In other words, it finds the simplest fraction in any given interval. Unlike other methods, it always finds the unique best answer, and uses the infinite precision of CAS long integers.

Example #1: What is the best fraction that's equal to \(\pi\pm\dfrac{1}{800}\)? Answer: \(\dfrac{179}{57}\), which is the same answer as given by Patrice's FareyDelta program. This is a difficult problem, because \(\dfrac{179}{57}\) is not a principal convergent of pi.

Example #2: What is the best fraction that's equal to \(\pi\pm\dfrac{1}{10^{14}}\)? Patrice's FareyDelta program can't handle that problem, because it is limited by the 12-digit accuracy of Home math. PDQ gets the right answer, though: \(\dfrac{58466453}{18610450}\).

Example #3: What is the best fraction for \(\pi\pm\dfrac{13131}{10^{440}}\)? PDQ returns the correct ratio of two huge integers (221 digits over 220 digits) in less than one second. (It takes the HP 50g over two minutes using System RPL). This is an extreme case, since the numbers are so huge, and once again the answer is not one of pi's principal convergents. Piece of cake for Prime+PDQ, which yields the unique correct answer:

PDQ calls another program called D2F (Decimal To Fraction) which returns the EXACT internal CAS representation of any real number.

Example: d2f(.5) returns \(\dfrac{1}{2}\), but d2f(.6) returns \(\dfrac{168884986026393}{2^{48}}\), because that's how Prime's CAS stores 0.6 internally.

Here's the source code for D2F (be sure to name it "d2f" in lowercase):

 LOCAL a,p,s,t,j; 
  IF x==0 THEN return(0);  END ;  
  IF type(x)==DOM_RAT THEN RETURN(abs(x));  END ;  
  IF type(x)==DOM_INT THEN RETURN(abs(x));  END ;  
  FOR j FROM 1 TO 3 DO  

And here's the code for PDQ (be sure to name it "pdq" in lowercase):

 LOCAL n,n0,d,d0,c,p,s,t1,t2,t3,a,b; 
  IF t==0 THEN  
    RETURN(d2f(j))  END ;  
  IF FP(t) AND (ABS(t)>1) THEN RETURN("Illegal Tolerance");  END ;  
  IF FP(t)==0 THEN t:=1/10^(exact(ABS(t)));  END ;  
  UNTIL (b*d)<=(c*a*d0);  

Instructions for PDQ:

Syntax: pdq(target, tolerance) where you want to find the best fraction for target +/- tolerance.

The target can be input as either a floating-point real number, or as a fraction (ratio of two integers). In the latter case, you may use as many digits as you wish, for extended precision.

The tolerance can also be a floating-point real number or a fraction, in which case it is used as-is. The tolerance can also be an integer n > 1, in which case it is converted to the exact ratio 1/10^n. This shortcut is handy for testing purposes.

PDQ not only returns its result, but also stores the exact error in 'err'. The output of PDQ, minus err, exactly equals PDQ's input target.

If the global variable 'ic' is non-zero when PDQ exits, then it is the number of intermediate convergents between the result and the next convergent. Example: pdq(pi,3) returns \(\dfrac{201}{64}\), and stores 6 into 'ic' because there are 6 intermediate convergents between \(\dfrac{201}{64}\) (inclusive) and the next convergent \(\dfrac{333}{106}\). "Intermediate convergents" are also known as "semiconvergents" and "secondary convergents".

Here is a handy variable for PDQ experimentation.

PI500 (This fraction's decimal expansion has the same first 500 decimal places as pi. Remove all carriage returns):

Examples (performed in CAS, not Home, for perfect accuracy):

• pdq(PI500,14) --> \(\dfrac{58466453}{18610450}\) (example #2 above).

• pdq(pi,14) --> \(\dfrac{47627751}{15160384}\) (different, because pi is not equal to PI500).

• d2f(pi) --> \(\dfrac{27633741218861}{2^{43}}\) (that's what pi is equal to in CAS).

• pdq(pi,14)-err --> same as d2f(pi).

• time(pdq(PI500,13131/alog10(440))) --> approximately 0.6 seconds.

Note: The exact( ) function in CAS has three severe shortcomings: (1) it only finds answers which are principal convergents; (2) it only allows the tolerance (epsilon) to lie between 10^-6 and 10^-14; and (3) it sometimes returns incorrect answers (outside the specified tolerance). PDQ has none of these shortcomings. It always finds the unique best answer for any target and tolerance.

Edit: added the warnings to name "d2f" and "pdq" in lowercase.

Edit: Changed example #3 at the beginning to a non-convergent. Thanks to Rodger Rosenbaum for pointing out this great example.

Edit: Both programs above have been revised slightly to reflect two minor changes in the Prime update released today, rev 6030:

exact(iPart(x)) is now IP(x)
frac(x) is now FP(x)

Edit (5 December 2014): Updated for compatibility with the greatly improved CAS program handling of Prime rev 6940. Also added the 'ic' variable for tracking intermediate convergents.

Visit this user's website Find all posts by this user
Quote this message in a reply
Post Reply 

Messages In This Thread
PDQ Algorithm: Infinite precision best fraction within tolerance - Joe Horn - 12-13-2013 05:09 AM

User(s) browsing this thread: 1 Guest(s)