Python 2, 110 байт
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
Максимальна кількість цифр для перевірки приймається з stdin. 10 000 цифр закінчується приблизно за 2 секунди за допомогою PyPy 5.3.
Використання зразка
$ echo 10000 | pypy pi-runs.py
3
33
111
9999
99999
999999
Щось корисне
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
Для цього я перейшов з Чудновського на Рамануджан 39. Чудновському не вистачило пам’яті в моїй системі незабаром після 100 мільйонів цифр, але Рамануджан домігся до 400 мільйонів, лише за 38 хвилин. Я думаю, це ще один випадок, коли врешті-решт темпи зростання термінів вигравали в кінцевому рахунку, принаймні, в системі з обмеженими ресурсами.
Використання зразка
$ 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
Швидші без обмежених генераторів
Довідкова реалізація, наведена в описі проблеми, цікава. Він використовує необмежений генератор, узятий безпосередньо з паперових алгоритмів Безмежного шпигуна для цифр Pi . За словами автора, надані реалізації є "навмисно неясними", тому я вирішив зробити нові реалізації всіх трьох алгоритмів, перерахованих автором, без навмисного придушення. Я також додав четвертий на основі Рамануджана №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)
Примітки
Вище наведено 6 реалізацій: дві базові реалізації, надані автором (позначаються _ref
), і чотири, які обчислюють терміни в партіях, генеруючи відразу кілька цифр ( _md
). Усі реалізації було підтверджено до 100 000 цифр. Вибираючи розміри партії, я вибрав значення, які з часом повільно втрачають точність. Наприклад, g1_md
генерує 10 цифр на партію з 33 ітераціями. Однак це дасть лише ~ 9,93 правильних цифр. Коли точність закінчиться, стан перевірки не вдасться, викликаючи додаткову партію для запуску. Це, здається, є більш ефективним, ніж повільно непомітно додаткова, непотрібна точність у часі.
- g1 (Лейбніц / Ейлер)
Додаткову змінну j
зберігають, що представляє 2*i+1
. Автор робить те ж саме в еталонній реалізації. Розрахунок n
окремо набагато простіше (і менш неясний), тому що він використовує поточні значення q
, r
і t
, а не на наступному.
- g2 (Ламберт)
Чекn == q/s
, мабуть, досить слабкий. Це має читати n == (q*(k+2*j+4)+r)/(s*(k+2*j+4)+t)
, де j
є 2*i-1
і k
є i*i
. При більш високих ітерацій r
і t
умови стають все менш значущими. Як і це, це добре для перших 100 000 цифр, тому, мабуть, це добре для всіх. Автор не надає посилання на реалізацію.
- g3 (Госпер)
Автор гадає, що зайве перевіряти, що n
не зміниться в наступних ітераціях, і що це служить лише для уповільнення роботи алгоритму. Хоча це, мабуть, правда, генератор тримається на ~ 13% правильніших цифр, ніж наразі, що здається дещо марнотратним. Я знову додав чек і чекаю, коли 50 цифр будуть правильними, генеруючи їх усі відразу, із помітним посиленням продуктивності.
- g4 (Рамануджан 39)
Розраховано як
На жаль, s
не випадає на нуль, завдяки первинному складу (3528 ÷), але він все ще значно швидший, ніж g3. Конвергенція становить ~ 5,89 цифр на термін, 3511 цифр формується за один раз. Якщо це трохи, генерування 271 цифри за 46 ітерацій також є гідним вибором.
Хронометраж
Зроблено в моїй системі лише для порівняння. Часи перераховані в секундах. Якщо час тривав більше 10 хвилин, я більше не проводив тестів.
| 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
Цікаво, що g2
зрештою наздоганяє g3
, незважаючи на повільніший темп конвергенції. Я підозрюю, що це тому, що операнди ростуть значно повільніше, виграючи в довгостроковій перспективі. Найшвидше g4_md
втілення приблизно на 235 разів швидше, ніж наg3_ref
на 500 000 цифр. Але це означає, що існує ще значна істотна витрата на передачу цифр таким чином. Розрахувати всі цифри безпосередньо за допомогою Ramanujan 39 ( джерело пітона ) приблизно 10 разів швидше.
Чому б не Чудновський?
Алгоритм Чудновського вимагає повноцінного квадратного кореня, я, чесно кажучи, не впевнений, як працювати - припускаючи, що це взагалі може бути. Рамануджан 39 дещо особливий у цьому плані. Однак метод здається, що він може сприяти формулам, подібним Махіну, таким як ті, які використовує y-cruncher, так що це може бути дорога, яку варто вивчити.