Python 2, 110 bayt
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
Kontrol edilecek maksimum basamak sayısı stdin'den alınmıştır. 10.000 basamak PyPy 5.3 ile yaklaşık 2 saniyede bitiyor.
Örnek Kullanımı
$ echo 10000 | pypy pi-runs.py
3
33
111
9999
99999
999999
Kullanışlı bir şey
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
Bunun için Chudnovsky'den Ramanujan 39'a geçtim. Chudnovsky, 100 milyon basamaktan kısa bir süre sonra sistemimde bellek kalmadı, ancak Ramanujan sadece 38 dakika içinde 400 milyona ulaştı. Bence bu, en azından sınırlı kaynaklara sahip bir sistemde, terimlerin yavaş büyüme oranının kazanmasıydı.
Örnek Kullanımı
$ 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
Daha Hızlı Sınırsız Jeneratörler
Sorun tanımında verilen referans uygulaması ilginçtir. Pi Basamakları için doğrudan Kağıt Bağlanmamış Spigot Algoritmaları'ndan alınan sınırsız bir jeneratör kullanır . Yazara göre, sağlanan uygulamalar "kasıtlı olarak belirsiz", bu yüzden kasıtlı gizleme olmadan yazar tarafından listelenen her üç algoritmanın da yeni uygulamalarını yapmaya karar verdim. Ayrıca Ramanujan # 39'a dayanan bir dördüncü ekledim .
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)
notlar
Yukarıda 6 uygulama vardır: yazar tarafından sağlanan iki referans uygulaması (belirtilen _ref
) ve toplu olarak terimleri hesaplayan ve aynı anda birden fazla basamak oluşturan dört uygulama ( _md
). Tüm uygulamalar 100.000 basamağa kadar onaylanmıştır. Parti boyutlarını seçerken, zaman içinde hassasiyeti yavaş yavaş kaybeden değerleri seçtim. Örneğin, g1_md
toplu iş başına 33 basamaklı 10 basamak oluşturur. Ancak, bu sadece ~ 9.93 doğru basamak üretir. Hassasiyet bittiğinde, kontrol koşulu başarısız olur ve fazladan bir parti çalıştırılmasını tetikler. Bu, zaman içinde yavaş yavaş ekstra ekstra, gereksiz hassasiyetten daha performanslı görünüyor.
- g1 (Leibniz / Euler) Temsil eden
fazladan bir değişken j
tutulur 2*i+1
. Yazar aynı şeyi referans uygulamasında da yapar. Hesaplama n
o anki değerlerini kullandığı için ayrı ayrı, çok daha basit (ve daha az belirsiz) 'dir q
, r
ve t
yerine gelecek daha.
- g2 (Lambert)
Çek n == q/s
kuşkusuz oldukça gevşek. Yani okumalı n == (q*(k+2*j+4)+r)/(s*(k+2*j+4)+t)
, nerede j
olduğunu 2*i-1
ve k
bir i*i
. Yüksek yinelemelerde r
ve t
terimleri giderek daha az önemli hale gelir. Olduğu gibi, ilk 100.000 basamak için iyidir, bu yüzden muhtemelen herkes için iyidir. Yazar hiçbir referans uygulaması sağlamaz.
- g3 (Gosper)
Yazar n
, sonraki yinelemelerde değişmeyeceğini kontrol etmenin gereksiz olduğunu ve sadece algoritmayı yavaşlatmaya hizmet ettiğini tahmin ediyor. Muhtemelen doğru olsa da, jeneratör şu anda ürettiğinden ~% 13 daha doğru rakamlar tutuyor, bu da biraz israf gibi görünüyor. Check-in işlemini tekrar ekledim ve 50 basamaklı doğru olana kadar bekleyin, hepsini bir kerede üretin, performansta gözle görülür bir kazanç sağlayın.
- g4 (Ramanujan 39)
olarak hesaplanan
Maalesef s
nedeniyle ilk (3528 ÷) bileşimine, sıfır dışarı vermez, ama g3 çok daha hızlı hala bu. Yakınsama her terim için ~ 5.89 hane, bir seferde 3511 hane oluşturulur. Bu biraz fazlaysa, 46 yineleme başına 271 basamak oluşturmak da iyi bir seçimdir.
zamanlamalar
Sadece karşılaştırma amacıyla sistemime alındı. Zamanlar saniye olarak listelenir. Bir zamanlama 10 dakikadan uzun sürerse, başka test yapmadım.
| 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
Yavaş bir yakınsama hızına rağmen , g2
sonunda geçmesi ilginçtir g3
. Bunun, işlenenlerin uzun vadede kazanarak önemli ölçüde daha yavaş bir oranda büyümesinden kaynaklandığından şüpheleniyorum. En hızlı uygulama g4_md
, g3_ref
500.000 basamak üzerindeki uygulamadan yaklaşık 235 kat daha hızlıdır . Bununla birlikte, bu şekilde akış rakamlarının hala önemli bir yükü var. Tüm basamakları doğrudan Ramanujan 39 ( python kaynağı ) kullanarak hesaplamak yaklaşık 10 kat daha hızlıdır.
Neden Chudnovsky olmasın?
Chudnovsky algoritması, tam olarak nasıl çalışılacağından emin olamadığım tam hassas bir kare kök gerektirir - hiç olmadığı varsayılır. Ramanujan 39 bu konuda biraz özeldir. Bununla birlikte, yöntem y-cruncher tarafından kullanılanlar gibi Machin benzeri formüllere elverişli görünebilir, bu yüzden keşfetmeye değer bir yol olabilir.