I saw this question and answer about calculating pi on Stack Overflow, and I decided to write my own program for calculating pi. I used Python and only integers (I didn't want to use floating point numbers), and used the Gauss–Legendre algorithm because it was the simplest to implement (I considered using the Borwein's algorithm, but I didn't want to calculate third roots of numbers, and the Chudnovsky algorithm seemed a little complicated, although maybe I'll give it a try). I would like to know how efficient are the algorithms above, in \$\mathcal{O}(n)\$ (n is the number of digits to calculate), and how efficient is my program?
# Calculate next square root approximation of the number.
def calculate_next_square_root(number, square_root):
next_square_root = ((number / square_root) + square_root) / 2
return next_square_root
# Calculate the square root of the number.
def calculate_square_root(number, digits, add):
# Start with 1, followed by (digits + add) zeros.
square_root = 1 * (10 ** (digits + add))
# Calculate next square root approximation of the number.
next_square_root = calculate_next_square_root(number=number, square_root=square_root)
while (next_square_root != square_root):
# Replace square root with next square root.
square_root = next_square_root
# Calculate next square root approximation of the number.
next_square_root = calculate_next_square_root(number=number, square_root=square_root)
return square_root
# Calculate next pi approximation.
def calculate_next_pi(a, b, t, digits, add):
next_pi = ((10 ** (digits + add)) * ((a + b) ** 2)) / (4 * t)
return next_pi
# Calculate pi to 5,000 digits.
digits = 5000
add = 500
a = 10 ** (digits + add)
b = calculate_square_root(number=(10 ** ((digits + add) * 2)) / 2, digits=digits, add=add)
t = (10 ** ((digits + add) * 2)) / 4
p = 1
pi = -1 # pi must be different than next_pi
next_pi = calculate_next_pi(a=a, b=b, t=t, digits=digits, add=add)
n = 0
while (next_pi != pi):
pi = next_pi
next_a = (a + b) / 2
next_b = calculate_square_root(number=a * b, digits=digits, add=add)
next_t = t - (p * ((a - next_a) ** 2))
next_p = 2 * p
a = next_a
b = next_b
t = next_t
p = next_p
next_pi = calculate_next_pi(a=a, b=b, t=t, digits=digits, add=add)
n += 1
# Remove the last 500 digits.
pi /= (10 ** add)
# Print the results.
print pi
print n
My program takes 52 lines of code (including comments), and I'm not interested in programs which take too many lines to implement, up to 100 or 200 lines are fine. I checked and it takes about one second to calculate 5,000 digits of pi (with add = 500
), and a little less than two minutes to calculate 50,000 digits (with add = 5000
). With 5,000 digits it took 14 iterations, and with 50,000 digits 17 iterations, but I didn't count how many iterations it took to calculate the square roots. Is my program efficiently using the Gauss–Legendre algorithm, or can it be made more efficient?
By the way, add
doesn't have to be 500, even 4 or 5 is enough. But I wanted to make sure all the digits of pi are correct (and they are). With add = 0
the last 2 or 3 digits are not correct.
I took the square root program from a program I wrote in 2013, but I don't know the name of the algorithm I used and whether it's the most efficient algorithm or not.
Update: I remind you my question: I would like to know how efficient are the algorithms above, in \$\mathcal{O}(n)\$ (n is the number of digits to calculate), and how efficient is my program? I prefer not to import modules, but use pure Python (either 2 or 3) without any import
. And of course if we start with a number closer to the square root (such as a or b) then it will take fewer iterations to calculate the square root.
echo 'scale=1000; 4*a(1)' | bc -l
\$\endgroup\$pypi
or something. =) \$\endgroup\$calculate_square_root
with**0.5
I get this error message: "OverflowError: long int too large to convert to float". \$\endgroup\$