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
Jumlah digit maksimum untuk diperiksa diambil dari stdin. 10.000 digit selesai dalam 2s dengan PyPy 5.3.
Contoh Penggunaan
$ echo 10000 | pypy pi-runs.py
3
33
111
9999
99999
999999
Sesuatu yang Berguna
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
Saya telah beralih dari Chudnovsky ke Ramanujan 39 untuk ini. Chudnovsky kehabisan memori pada sistem saya tak lama setelah 100 juta digit, tetapi Ramanujan mencapai 400 juta, hanya dalam waktu 38 menit. Saya pikir ini adalah kasus lain adalah tingkat pertumbuhan yang lebih lambat menang pada akhirnya, setidaknya pada sistem dengan sumber daya terbatas.
Contoh Penggunaan
$ 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
Generator Tanpa Batas Lebih Cepat
Implementasi referensi yang diberikan dalam deskripsi masalah menarik. Ini menggunakan generator tanpa batas, diambil langsung dari kertas Algoritma Keran Tanpa Batas untuk Digit Pi . Menurut penulis, implementasi yang disediakan adalah "sengaja tidak jelas", jadi saya memutuskan untuk membuat implementasi baru dari ketiga algoritma yang terdaftar oleh penulis, tanpa kebingungan yang disengaja. Saya juga menambahkan yang keempat, berdasarkan 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)
Catatan
Di atas adalah 6 implementasi: dua implementasi referensi yang disediakan oleh penulis (dilambangkan _ref
), dan empat yang menghitung istilah dalam batch, menghasilkan beberapa digit sekaligus (_md
). Semua implementasi telah dikonfirmasi hingga 100.000 digit. Saat memilih ukuran batch, saya memilih nilai yang secara perlahan kehilangan presisi seiring waktu. Misalnya, g1_md
menghasilkan 10 digit per batch, dengan 33 iterasi. Namun, ini hanya akan menghasilkan ~ 9,93 digit yang benar. Ketika presisi habis, kondisi pemeriksaan akan gagal, memicu bets tambahan untuk dijalankan. Ini tampaknya lebih berkinerja daripada mendapatkan secara ekstra, presisi yang tidak dibutuhkan seiring waktu.
- g1 (Leibniz / Euler)
Suatu variabel tambahan j
disimpan, mewakili 2*i+1
. Penulis melakukan hal yang sama dalam implementasi referensi. Menghitung n
secara terpisah jauh lebih sederhana (dan kurang jelas), karena menggunakan nilai saat ini q
,r
dan t
, daripada yang berikutnya.
- g2 (Lambert)
Cek n == q/s
ini diakui cukup longgar. Itu harus membaca n == (q*(k+2*j+4)+r)/(s*(k+2*j+4)+t)
, di mana j
adalah 2*i-1
dan k
adalah i*i
. Pada iterasi yang lebih tinggi, r
dan t
istilah menjadi semakin kurang signifikan. Seperti, ini bagus untuk 100.000 digit pertama, jadi mungkin bagus untuk semua. Penulis tidak memberikan implementasi referensi.
- g3 (Gosper)
Penulis menduga bahwa tidak perlu memeriksa yang n
tidak akan berubah dalam iterasi berikutnya, dan bahwa itu hanya berfungsi untuk memperlambat algoritma. Meskipun mungkin benar, generator memegang ~ 13% angka yang lebih benar daripada yang dihasilkan saat ini, yang tampaknya agak boros. Saya telah menambahkan check-in kembali, dan menunggu hingga 50 digit benar, menghasilkan semuanya sekaligus, dengan perolehan kinerja yang nyata.
- g4 (Ramanujan 39)
Dihitung sebagai
Sayangnya, s
tidak nol, karena komposisi awal (3528 ÷), tetapi masih secara signifikan lebih cepat daripada g3. Konvergensi adalah ~ 5,89 digit per term, 3511 digit dihasilkan pada suatu waktu. Jika itu sedikit banyak, menghasilkan 271 digit per 46 iterasi juga merupakan pilihan yang layak.
Pengaturan waktu
Diambil di sistem saya, hanya untuk tujuan perbandingan. Waktu tercantum dalam hitungan detik. Jika waktu yang dibutuhkan lebih dari 10 menit, saya tidak menjalankan tes lebih lanjut.
| 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
Sangat menarik yang g2
akhirnya menyusul g3
, meskipun tingkat konvergensi lebih lambat. Saya menduga ini karena operan tumbuh pada tingkat yang secara signifikan lebih lambat, menang dalam jangka panjang. g4_md
Implmentasi tercepat adalah sekitar 235x lebih cepat daripada g3_ref
implmentasi pada 500.000 digit. Yang mengatakan, masih ada overhead yang signifikan untuk streaming digit dengan cara ini. Menghitung semua digit secara langsung menggunakan Ramanujan 39 ( sumber python ) kira-kira 10x lebih cepat.
Kenapa tidak Chudnovsky?
Algoritma Chudnovsky membutuhkan akar kuadrat presisi penuh, yang sejujurnya saya tidak yakin bagaimana cara kerjanya - dengan asumsi bisa saja sama sekali. Ramanujan 39 agak istimewa dalam hal ini. Namun, metode ini sepertinya kondusif untuk formula mirip Machin, seperti yang digunakan oleh y-cruncher, sehingga mungkin jalan yang layak dijelajahi.