-
-
Notifications
You must be signed in to change notification settings - Fork 46.6k
/
Copy pathpi_generator.py
93 lines (73 loc) · 2.76 KB
/
pi_generator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def calculate_pi(limit: int) -> str:
"""
https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80
Leibniz Formula for Pi
The Leibniz formula is the special case arctan 1 = 1/4 Pi .
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
The errors can in fact be predicted;
but those calculations also approach infinity for accuracy.
Our output will always be a string since we can defintely store all digits in there.
For simplicity' sake, let's just compare against known values and since our outpit
is a string, we need to convert to float.
>>> import math
>>> float(calculate_pi(15)) == math.pi
True
To further proof, since we cannot predict errors or interrupt any infinite alternating series generation since they approach infinity,
or interrupt any alternating series, we are going to need math.isclose()
>>> math.isclose(float(calculate_pi(50)), math.pi)
True
>>> math.isclose(float(calculate_pi(100)), math.pi)
True
Since the math.pi-constant is only 16 digits long, here some test with preknown values:
>>> calculate_pi(50)
'3.14159265358979323846264338327950288419716939937510'
>>> calculate_pi(80)
'3.14159265358979323846264338327950288419716939937510582097494459230781640628620899'
In the Leibniz formula for calculating pi, the variables q, r, t, k, n, and l are used for the iteration process.
""" # noqa: E501
q = 1
r = 0
t = 1
k = 1
n = 3
l = 3
decimal = limit
counter = 0
result = ""
"""
We will avoid using yield since we otherwise get a Generator-Object,
which we can't just compare against anything. We would have to make a list out of it
after the generation, so we will just stick to plain return logic:
"""
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:
nr = (2 * q + r) * l
nn = (q * (7 * k) + 2 + (r * l)) // (t * l)
q *= k
t *= l
l += 2
k += 1
n = nn
r = nr
return result
def main() -> None:
pi_digits = calculate_pi(50)
print(pi_digits)
import doctest
doctest.testmod()
if __name__ == "__main__":
main()