An algorithm for approximating decimals of pi is raised in a recent question . The algorithm basically boils down to iterating the following recurrence relation:
num_iter = 10
a = 0
i = 1
while i <= num_iter:
a = sqrt(2+a)
x = sqrt(2-a)
pi = x * 2**(i+1)
i += 1
This relation works well for low values of num_iter
, but rounding errors begin to occur, which was the objective of that question, and which can be corrected using fixed-point representations ( decimal
), at the cost of increasing the memory needed.
My question now is what algorithm is this that is being used? I couldn't find references to it. On the other hand it doesn't seem to be a very good algorithm (as was shown in the answer to that question) What other algorithms are better for computing pi? In what way are they better?
The algorithm used in that question is not one of those that Wikipedia shows in its entry on approximations of pi.
Mathematically this algorithm can be expressed by the following recurrence relation:
which can be more easily visualized if we use the "expanded" version for the cases n=2 and n=3 for example:
The only reference I've found to this approximation of pi comes from an answer on Math.stackexchange , which itself was simply a copy of a Facebook post, which included the following image:
Therefore it looks like a geometric approximation, designed to be easy to visualize and not precisely efficient. There are much more efficient approaches of which I will highlight just a couple (because the topic of computing pi efficiently is certainly a world).
Gauss–Legendre approximation
This approximation converges much faster than the one used in this question. In the one used in this question, the number of correct decimals only increases by 1 (or sometimes 0) with each new iteration. On the other hand, in the Gauss-Legendre approximation, the number of correct digits doubles in each iteration, so that after a few iterations there is already a high number of decimal places.
The mathematical details are complex (they can be found on Wikipedia ), and here I will give only a Python implementation (adapted from this answer on SO ):
With only three iterations, many correct decimals are already obtained!
And with one more iteration, the number of correct decimal places doubles.
already with five or more we have the 64 correct digits (the last digit is never correct due to the rounding error caused by working only with 65 decimals)
In fact, a small variation of the above program can allow us to obtain any desired number of decimal places. It is enough to work with a precision of a couple more decimal places (for example, if we want 500 digits of pi, we work with 502) to eliminate this problem in the final rounding. And instead of iterating a preset number of times, we iterate until two successive iterations produce the same result, then stopping:
Let's go big!
Algorithms based on getting figures
If the previous algorithm is based on approximations (each iteration produces a number closer to pi), there are other algorithms that are based on obtaining numbers of pi. The main advantage of this type of algorithm would be that they do not require as much storage space, because once a digit has been computed, it is printed and it is not necessary to keep all the previous digits in memory when computing the next one. This is an advantage over methods such as Gauss-Legendre seen before, in which if we want to obtain a precision of one million digits, we must work with a million digits from the beginning.
One very notable algorithm is the one based on the Bailey-Borwein-Plouffe formula , since it allows computing the digit that appears in position N without the need to compute the previous N-1. Unfortunately that algorithm produces the digits in hexadecimal base, and not in the base 10 that we are used to. Knowing the Nth digit in hexadecimal does not help us to know a specific digit of the decimal expansion. What is possible is to write pi with N digits (hexadecimal), which would be an approximation of pi, and convert that number to decimal, to obtain another approximation in base 10, which would be correct with a precision of 2 -4*N
However, this saving the first N hexadecimal digits to pass them to decimal spoils that advantage of "not having to compute or store the previous digits".
Other methods of obtaining pi "digit by digit" are known globally as Spigot algorithms . One of them (which I've adapted from this answer on SO ), claims to be quite fast, although it's not at all obvious to understand what it does (not even reading the article it's based on), so it could even be wrong, although it seems to give correctly the first 65 decimals:
Execution result:
This method does not converge as fast as the one seen before, since to obtain 65 decimal places it has iterated 65 times. The function
pi_digits()
is an infinite generator, each time it is called it returns the next digit of pi. The one Ifor
use it from has abreak
to stop after 65 calls (otherwise it would be infinite).The advantage of this method is that it does not depend on precision nor does it require
decimal
, since it works with integers, and that it is more efficient in memory since it does not need to retain all the digits of pi that it has computed so far, but only a few variables (p,q,k,a,b,a1,1,d,d1
) . . In return it is slower from a certain decimal.If we put it to compute 1000 digits we see that it obtains exactly the same result as the Gauss-Legendre algorithm, and it takes twice as long. If we try to get 100,000 digits of pi, Gauss-Legendre takes 21s, while the Spigot algorithm takes on the order of 3 minutes. Each additional digit takes a little longer.