計算

引言

這篇是對此前一篇的補充。

代碼

注意python小數精度問題,默認是十七位,這顯然是不夠的,按照文中的公式也只能計算到這個位次附近。需要借助一些庫,譬如mpmath

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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from time import time
from mpmath import *

precision = 50

mp.dps = precision
mp.pretty = True

sqrt_3 = sqrt(3)
base_9_list = []
base_10_list = []

def bbp_fraction(integer, n):
numerator = mpf(16)*sqrt_3*(integer+1)
denominator = mpf(9)**(integer-n+1)*mpf(3)*(mpf(16)*(integer**2+integer)+mpf(3))
return fmod(numerator/denominator, 1)

def base_9_fraction(integer, k):
numerator = base_9_list[integer] * mpf(10)**(k-1)
denominator = mpf(9)**integer
return fmod(numerator/denominator, 1)

def kappa_n(n, epsilon):
numerator = n*log(9) - 2*log(2) - 0.5*log(3) - log(epsilon)
denominator = log(9)
return ceil(numerator / denominator)

def kappa_k(k, epsilon):
numerator = k*log(10) - log(epsilon)
denominator = log(9)
return ceil(numerator / denominator)

class Processor:
def __init__(self, base):
self.base = base

def epsilon(self, sum_fraction):
return 1 - fmod(self.base*sum_fraction, 1)

def digit(self, sum_fraction):
return int(floor(self.base*sum_fraction))

def print(self):
base_list = eval('base_%s_list' % self.base)
number_of_lines = int(ceil((len(base_list)-1) / 10))
print('Pi in base %s to %s decimal places.\n\n' %(self.base, len(base_list)-1))
print('%s. ' % base_list[0])
for i in range(number_of_lines):
if i == number_of_lines - 1:
sub_list = base_list[1+i*10:]
else:
sub_list = base_list[1+i*10:1+(i+1)*10]
sub_list_string = ' '.join(map(str, sub_list))
print(' ' + sub_list_string + '\n')

def main():
P1 = Processor(9)
P2 = Processor(10)

# Pi in base 9.
t1 = time()
for i in range(precision):
j = 0
sum_fraction = mpf(0)

while True:
sum_fraction += bbp_fraction(j, i)
sum_fraction = fmod(sum_fraction, 1)
epsilon = P1.epsilon(sum_fraction)
kappa = kappa_n(i, epsilon)

if kappa <= j:
base_9_list.append(P1.digit(sum_fraction))
break
j += 1
t2 = time()
P1.print()
print('Time taken: %.4f seconds.\n\n' %(t2 - t1))

# Pi in base 10.
t3 = time()
for i in range(precision):
j = 0
sum_fraction = mpf(0)
out_of_range = False

while True:
if j == precision:
out_of_range = True
break

sum_fraction += base_9_fraction(j, i)
sum_fraction = fmod(sum_fraction, 1)
epsilon = P2.epsilon(sum_fraction)
kappa = kappa_k(i, epsilon)

if kappa <= j:
base_10_list.append(P2.digit(sum_fraction))
break
j += 1

if out_of_range:
break
t4 = time()
P2.print()
print('Time taken: %.4f seconds.\n\n' %(t4 - t3))

if __name__ == '__main__':
main()

輸出的結果為

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
Pi in base 9 to 49 decimal places.


3.
1 2 4 1 8 8 1 2 4 0

7 4 4 2 7 8 8 6 4 5

1 7 7 7 6 1 7 3 1 0

3 5 8 2 8 5 1 6 5 4

5 3 5 3 4 6 2 6 5

Time taken: 0.2193 seconds.


Pi in base 10 to 46 decimal places.


3.
1 4 1 5 9 2 6 5 3 5

8 9 7 9 3 2 3 8 4 6

2 6 4 3 3 8 3 2 7 9

5 0 2 8 8 4 1 9 7 1

6 9 3 9 9 3

Time taken: 0.1335 seconds.

後記

優點在於不依賴以往的結果,對九進制的每一位精確地知道需要算到第幾階,而且知道結果一定是對的,更不需要去做比較。