2023-04-18 09:57:48 +00:00
|
|
|
def calculate_pi(limit: int) -> str:
|
|
|
|
"""
|
|
|
|
https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80
|
|
|
|
Leibniz Formula for Pi
|
|
|
|
|
2023-10-07 19:32:28 +00:00
|
|
|
The Leibniz formula is the special case arctan(1) = pi / 4.
|
2023-04-18 09:57:48 +00:00
|
|
|
Leibniz's formula converges extremely slowly: it exhibits sublinear convergence.
|
|
|
|
|
|
|
|
Convergence (https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Convergence)
|
|
|
|
|
|
|
|
We cannot try to prove against an interrupted, uncompleted generation.
|
|
|
|
https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80#Unusual_behaviour
|
2023-10-07 19:32:28 +00:00
|
|
|
The errors can in fact be predicted, but those calculations also approach infinity
|
|
|
|
for accuracy.
|
2023-04-18 09:57:48 +00:00
|
|
|
|
2023-10-07 19:32:28 +00:00
|
|
|
Our output will be a string so that we can definitely store all digits.
|
2023-04-18 09:57:48 +00:00
|
|
|
|
|
|
|
>>> import math
|
|
|
|
>>> float(calculate_pi(15)) == math.pi
|
|
|
|
True
|
|
|
|
|
2023-10-07 19:32:28 +00:00
|
|
|
Since we cannot predict errors or interrupt any infinite alternating series
|
|
|
|
generation since they approach infinity, or interrupt any alternating series, we'll
|
|
|
|
need math.isclose()
|
2023-04-18 09:57:48 +00:00
|
|
|
|
|
|
|
>>> math.isclose(float(calculate_pi(50)), math.pi)
|
|
|
|
True
|
|
|
|
>>> math.isclose(float(calculate_pi(100)), math.pi)
|
|
|
|
True
|
|
|
|
|
2023-10-07 19:32:28 +00:00
|
|
|
Since math.pi contains only 16 digits, here are some tests with known values:
|
2023-04-18 09:57:48 +00:00
|
|
|
|
|
|
|
>>> calculate_pi(50)
|
|
|
|
'3.14159265358979323846264338327950288419716939937510'
|
|
|
|
>>> calculate_pi(80)
|
|
|
|
'3.14159265358979323846264338327950288419716939937510582097494459230781640628620899'
|
|
|
|
"""
|
2023-10-07 19:32:28 +00:00
|
|
|
# Variables used for the iteration process
|
2023-04-18 09:57:48 +00:00
|
|
|
q = 1
|
|
|
|
r = 0
|
|
|
|
t = 1
|
|
|
|
k = 1
|
|
|
|
n = 3
|
2024-04-19 19:30:22 +00:00
|
|
|
m = 3
|
2023-10-07 19:32:28 +00:00
|
|
|
|
2023-04-18 09:57:48 +00:00
|
|
|
decimal = limit
|
|
|
|
counter = 0
|
|
|
|
|
|
|
|
result = ""
|
|
|
|
|
2023-10-07 19:32:28 +00:00
|
|
|
# We can't compare against anything if we make a generator,
|
|
|
|
# so we'll stick with plain return logic
|
2023-04-18 09:57:48 +00:00
|
|
|
while counter != decimal + 1:
|
|
|
|
if 4 * q + r - t < n * t:
|
|
|
|
result += str(n)
|
|
|
|
if counter == 0:
|
|
|
|
result += "."
|
|
|
|
|
|
|
|
if decimal == counter:
|
|
|
|
break
|
|
|
|
|
|
|
|
counter += 1
|
|
|
|
nr = 10 * (r - n * t)
|
|
|
|
n = ((10 * (3 * q + r)) // t) - 10 * n
|
|
|
|
q *= 10
|
|
|
|
r = nr
|
|
|
|
else:
|
2024-04-19 19:30:22 +00:00
|
|
|
nr = (2 * q + r) * m
|
|
|
|
nn = (q * (7 * k) + 2 + (r * m)) // (t * m)
|
2023-04-18 09:57:48 +00:00
|
|
|
q *= k
|
2024-04-19 19:30:22 +00:00
|
|
|
t *= m
|
|
|
|
m += 2
|
2023-04-18 09:57:48 +00:00
|
|
|
k += 1
|
|
|
|
n = nn
|
|
|
|
r = nr
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
|
|
print(f"{calculate_pi(50) = }")
|
|
|
|
import doctest
|
|
|
|
|
|
|
|
doctest.testmod()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|