Python 2, 110 byte
n=input()
x=p=7*n|1
while~-p:x=p/2*x/p+2*10**n;p-=2
l=m=0
for c in`x`:
l=l*(p==c)+1;p=c
if l>m:m=l;print p*l
Số chữ số tối đa để kiểm tra được lấy từ stdin. 10.000 chữ số kết thúc sau khoảng 2 giây với PyPy 5.3.
Sử dụng mẫu
$ echo 10000 | pypy pi-runs.py
3
33
111
9999
99999
999999
Một vài thứ hữu ích
from sys import argv
from gmpy2 import mpz
def pibs(a, b):
if a == b:
if a == 0:
return (1, 1, 1123)
p = a*(a*(32*a-48)+22)-3
q = a*a*a*24893568
t = 21460*a+1123
return (p, -q, p*t)
m = (a+b) >> 1
p1, q1, t1 = pibs(a, m)
p2, q2, t2 = pibs(m+1, b)
return (p1*p2, q1*q2, q2*t1 + p1*t2)
if __name__ == '__main__':
from sys import argv
digits = int(argv[1])
pi_terms = mpz(digits*0.16975227728583067)
p, q, t = pibs(0, pi_terms)
z = mpz(10)**digits
pi = 3528*q*z/t
l=m=0
x=0
for c in str(pi):
l=l*(p==c)+1;p=c
if l>m:m=l;print x,p*l
x+=1
Tôi đã chuyển từ Chudnovsky sang Ramanujan 39 cho việc này. Chudnovsky đã hết bộ nhớ trên hệ thống của tôi ngay sau 100 triệu chữ số, nhưng Ramanujan đã đạt được con số 400 triệu, chỉ trong khoảng 38 phút. Tôi nghĩ rằng đây là một trường hợp khác là tốc độ tăng trưởng chậm hơn của các điều khoản cuối cùng, ít nhất là trên một hệ thống có nguồn lực hạn chế.
Sử dụng mẫu
$ python pi-ramanujan39-runs.py 400000000
0 3
25 33
155 111
765 9999
766 99999
767 999999
710106 3333333
22931752 44444444
24658609 777777777
386980421 6666666666
Máy phát điện không giới hạn nhanh hơn
Việc thực hiện tham chiếu được đưa ra trong mô tả vấn đề là thú vị. Nó sử dụng một trình tạo không giới hạn, được lấy trực tiếp từ thuật toán Spigot không giới hạn trên giấy cho các chữ số của Pi . Theo tác giả, các triển khai được cung cấp là "cố tình che khuất", vì vậy tôi quyết định thực hiện các triển khai mới của cả ba thuật toán được liệt kê bởi tác giả, mà không cố tình che giấu. Tôi cũng đã thêm một phần tư, dựa trên Ramanujan # 39 .
try:
from gmpy2 import mpz
except:
mpz = long
def g1_ref():
# Leibniz/Euler, reference
q, r, t = mpz(1), mpz(0), mpz(1)
i, j = 1, 3
while True:
n = (q+r)/t
if n*t > 4*q+r-t:
yield n
q, r = 10*q, 10*(r-n*t)
q, r, t = q*i, (2*q+r)*j, t*j
i += 1; j += 2
def g1_md():
# Leibniz/Euler, multi-digit
q, r, t = mpz(1), mpz(0), mpz(1)
i, j = 1, 3
z = mpz(10)**10
while True:
n = (q+r)/t
if n*t > 4*q+r-t:
for d in digits(n, i>34 and 10 or 1): yield d
q, r = z*q, z*(r-n*t)
u, v, x = 1, 0, 1
for k in range(33):
u, v, x = u*i, (2*u+v)*j, x*j
i += 1; j += 2
q, r, t = q*u, q*v+r*x, t*x
def g2_md():
# Lambert, multi-digit
q, r, s, t = mpz(0), mpz(4), mpz(1), mpz(0)
i, j, k = 1, 1, 1
z = mpz(10)**49
while True:
n = (q+r)/(s+t)
if n == q/s:
for d in digits(n, i>65 and 49 or 1): yield d
q, r = z*(q-n*s), z*(r-n*t)
u, v, w, x = 1, 0, 0, 1
for l in range(64):
u, v, w, x = u*j+v, u*k, w*j+x, w*k
i += 1; j += 2; k += j
q, r, s, t = q*u+r*w, q*v+r*x, s*u+t*w, s*v+t*x
def g3_ref():
# Gosper, reference
q, r, t = mpz(1), mpz(180), mpz(60)
i = 2
while True:
u, y = i*(i*27+27)+6, (q+r)/t
yield y
q, r, t, i = 10*q*i*(2*i-1), 10*u*(q*(5*i-2)+r-y*t), t*u, i+1
def g3_md():
# Gosper, multi-digit
q, r, t = mpz(1), mpz(0), mpz(1)
i, j = 1, 60
z = mpz(10)**50
while True:
n = (q+r)/t
if n*t > 6*i*q+r-t:
for d in digits(n, i>38 and 50 or 1): yield d
q, r = z*q, z*(r-n*t)
u, v, x = 1, 0, 1
for k in range(37):
u, v, x = u*i*(2*i-1), j*(u*(5*i-2)+v), x*j
i += 1; j += 54*i
q, r, t = q*u, q*v+r*x, t*x
def g4_md():
# Ramanujan 39, multi-digit
q, r, s ,t = mpz(0), mpz(3528), mpz(1), mpz(0)
i = 1
z = mpz(10)**3511
while True:
n = (q+r)/(s+t)
if n == (22583*i*q+r)/(22583*i*s+t):
for d in digits(n, i>597 and 3511 or 1): yield d
q, r = z*(q-n*s), z*(r-n*t)
u, v, x = mpz(1), mpz(0), mpz(1)
for k in range(596):
c, d, f = i*(i*(i*32-48)+22)-3, 21460*i-20337, -i*i*i*24893568
u, v, x = u*c, (u*d+v)*f, x*f
i += 1
q, r, s, t = q*u, q*v+r*x, s*u, s*v+t*x
def digits(x, n):
o = []
for k in range(n):
x, r = divmod(x, 10)
o.append(r)
return reversed(o)
Ghi chú
Trên đây là 6 triển khai: hai triển khai tham chiếu được cung cấp bởi tác giả (ký hiệu _ref
) và bốn triển khai tính toán các thuật ngữ theo lô, tạo ra nhiều chữ số cùng một lúc ( _md
). Tất cả các triển khai đã được xác nhận đến 100.000 chữ số. Khi chọn kích thước lô, tôi chọn các giá trị từ từ mất độ chính xác theo thời gian. Ví dụ: g1_md
tạo 10 chữ số mỗi lô, với 33 lần lặp. Tuy nhiên, điều này sẽ chỉ tạo ra ~ 9,93 chữ số chính xác. Khi độ chính xác hết, điều kiện kiểm tra sẽ thất bại, kích hoạt một lô bổ sung được chạy. Điều này dường như được thực hiện nhiều hơn là từ từ tăng thêm, độ chính xác không cần thiết theo thời gian.
- g1 (Leibniz / Euler)
Một biến phụ j
được giữ, đại diện 2*i+1
. Các tác giả làm tương tự trong việc thực hiện tham khảo. Tính toán n
riêng biệt đơn giản hơn nhiều (và ít tối nghĩa hơn), bởi vì nó sử dụng các giá trị hiện tại của q
, r
và t
, thay vì tiếp theo.
- g2 (Lambert)
Việc kiểm tra n == q/s
được thừa nhận khá lỏng lẻo. Đó là nên đọc n == (q*(k+2*j+4)+r)/(s*(k+2*j+4)+t)
, nơi j
là 2*i-1
và k
là i*i
. Ở các lần lặp cao hơn, các thuật ngữ r
và t
ngày càng trở nên ít quan trọng hơn. Như vậy, nó tốt cho 100.000 chữ số đầu tiên, vì vậy nó có thể tốt cho tất cả. Tác giả cung cấp không thực hiện tham khảo.
- g3 (Gosper)
Tác giả phỏng đoán rằng không cần thiết phải kiểm tra xem nó n
sẽ không thay đổi trong các lần lặp lại tiếp theo hay không và nó chỉ phục vụ để làm chậm thuật toán. Mặc dù có lẽ đúng, nhưng trình tạo này đang giữ các chữ số chính xác hơn ~ 13% so với hiện tại, điều này có vẻ hơi lãng phí. Tôi đã thêm kiểm tra lại và đợi cho đến khi 50 chữ số chính xác, tạo ra tất cả chúng cùng một lúc, với hiệu suất đáng chú ý.
- g4 (Ramanujan 39) Được
tính là
Thật không may, s
không bằng không, do thành phần ban đầu (3528), nhưng nó vẫn nhanh hơn đáng kể so với g3. Hội tụ là ~ 5,89 chữ số cho mỗi thuật ngữ, 3511 chữ số được tạo tại một thời điểm. Nếu đó là một chút nhiều, tạo ra 271 chữ số trên 46 lần lặp cũng là một lựa chọn hợp lý.
Thời gian
Lấy trên hệ thống của tôi, chỉ nhằm mục đích so sánh. Thời gian được liệt kê trong vài giây. Nếu thời gian kéo dài hơn 10 phút, tôi đã không chạy thêm bất kỳ bài kiểm tra nào.
| g1_ref | g1_md | g2_md | g3_ref | g3_md | g4_md
------------+---------+---------+---------+---------+---------+--------
10,000 | 1.645 | 0.229 | 0.093 | 0.312 | 0.062 | 0.062
20,000 | 6.859 | 0.937 | 0.234 | 1.140 | 0.250 | 0.109
50,000 | 55.62 | 5.546 | 1.437 | 9.703 | 1.468 | 0.234
100,000 | 247.9 | 24.42 | 5.812 | 39.32 | 5.765 | 0.593
200,000 | 2,158 | 158.7 | 25.73 | 174.5 | 33.62 | 2.156
500,000 | - | 1,270 | 215.5 | 3,173 | 874.8 | 13.51
1,000,000 | - | - | 1,019 | - | - | 58.02
Thật thú vị khi g2
cuối cùng đã vượt qua g3
, mặc dù tốc độ hội tụ chậm hơn. Tôi nghi ngờ điều này là do các toán hạng phát triển với tốc độ chậm hơn đáng kể, chiến thắng trong thời gian dài. g4_md
Cấy ghép nhanh nhất là nhanh hơn khoảng 235 lần so với cấy ghép g3_ref
trên 500.000 chữ số. Điều đó nói rằng, vẫn còn một chi phí đáng kể để truyền các chữ số theo cách này. Tính toán tất cả các chữ số trực tiếp bằng Ramanujan 39 ( nguồn python ) nhanh gấp khoảng 10 lần.
Tại sao không phải là Chudnovsky?
Thuật toán Chudnovsky yêu cầu một căn bậc hai chính xác đầy đủ, mà tôi thực sự không chắc chắn làm thế nào để làm việc - giả sử nó có thể là tất cả. Ramanujan 39 có phần đặc biệt về vấn đề này. Tuy nhiên, phương pháp này có vẻ có lợi cho các công thức giống Machin, chẳng hạn như các công thức được sử dụng bởi y-cruncher, vì vậy đó có thể là một con đường đáng để khám phá.